Hardware FixRecommendedDevice not working? Your driver may be the problemCheck updates for common hardware issues.Fix DriversOctober DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsClean PCRecommendedOne scan can reveal what keeps slowing WindowsLook for cleanup and repair opportunities.Run Scan×
Skip to content
World desk5 min

Build a JavaScript Text Checker with Accurate Counts and Fresh Results

A dependable JavaScript text checker counts grapheme clusters for readers, keeps UTF-16 offsets for source operations, and rejects analysis results from outdated input.
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

A reliable text checker should keep three concerns separate: what it means by a “character,” where a finding sits in the original string, and whether an analysis result still belongs to the latest input. For a human-facing count, use grapheme clusters; for JavaScript selections and replacements, keep UTF-16 code-unit offsets; and for asynchronous analysis, commit results only if they match the current input revision.

What does “character count” mean in JavaScript?

There is no single count that answers every text-checker question. JavaScript strings are indexed in UTF-16 code units, while Unicode code points, user-perceived characters, and encoded bytes are different units.

As an Amazon Associate I earn from qualifying purchases.

Measure Use it for JavaScript approach Important limit
UTF-16 code units String indexing, slicing, and source offsets text.length and JavaScript string indexes A supplementary-plane code point uses two code units.
Unicode code points Counting scalar values in a well-formed string [...text].length Combining marks and emoji sequences can contain multiple code points but appear as one perceived character.
Extended grapheme clusters A human-facing approximation of “characters” Intl.Segmenter with granularity: "grapheme" Standard boundary rules are an approximation; implementations may declare a profile.
UTF-8 bytes Encoded storage, transmission, or byte-based limits new TextEncoder().encode(text).length Bytes are not characters, and the count depends on encoding.

For example, MDN documents that a supplementary-plane symbol such as the fox emoji takes two UTF-16 code units, so "🦊".length is 2 even though it is one code point and is ordinarily perceived as one character. A base letter followed by a combining mark, or an emoji joined with another emoji, can also form one visible cluster from multiple code points. MDN explains JavaScript string length and Unicode Standard Annex #29 defines default grapheme-cluster boundaries.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Unicode Standard Annex #29 Version 49, for Unicode 18.0.0, is dated 2026-09-01. It notes: “A single Unicode code point is often, but not always the same as a basic unit of a writing system for a language, or what a typical user might think of as a ‘character’.” The annex specifies default extended-grapheme rules and permits implementations to declare a profile with precisely specified changes. A grapheme count is therefore a useful, standards-based approximation, not a promise to match every reader’s idea of a character.

How should a checker count graphemes and preserve source offsets?

Use grapheme segmentation for the displayed human-facing count, but keep each finding’s range in UTF-16 code units. JavaScript slicing and most string operations use those offsets, and Intl.Segmenter segment records expose an index in the same coordinate system. The end of a segment is segment.index + segment.segment.length.

function inspectText(text, locale = "en") {
  const segmenter = new Intl.Segmenter(locale, { granularity: "grapheme" });
  const segments = [...segmenter.segment(text)];

  return {
    graphemeCount: segments.length,
    ranges: segments.map(({ segment, index }) => ({
      start: index,
      end: index + segment.length
    }))
  };
}

Here, graphemeCount counts perceived-character approximations, while each range remains usable with operations such as text.slice(start, end). Do not convert offsets into code-point indexes unless the rest of the checker is designed to use that coordinate system.

Keep the original input as the source of truth. If analysis normalizes text, changes case, strips punctuation, or otherwise transforms it, positions in the transformed string may no longer identify the same span in the original. When transformed analysis is necessary, retain a mapping back to original UTF-16 positions; the mapping method depends on the transformation and is not supplied by Intl.Segmenter.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Intl.Segmenter is marked Baseline 2024 by MDN, with availability across the latest browser versions since April 2024. That designation does not cover every older browser or embedded runtime, so feature-detect it when those environments matter. MDN’s Intl.Segmenter reference documents the API and its segmentation behavior.

When should the checker count words, sentences, or bytes?

Word counts

Use Intl.Segmenter with granularity: "word" when locale-sensitive boundaries matter. A common product definition is to count only segments whose isWordLike property is true. Make the locale explicit, or explain how the checker selects it; word boundaries depend on language conventions.

function countWords(text, locale = "en") {
  const segmenter = new Intl.Segmenter(locale, { granularity: "word" });
  let count = 0;

  for (const part of segmenter.segment(text)) {
    if (part.isWordLike) count++;
  }

  return count;
}

Splitting on whitespace is not a universal tokenizer. MDN identifies Japanese, Chinese, Thai, Lao, Khmer, and Myanmar among languages for which spaces do not reliably separate words. MDN documents locale-sensitive word segmentation.

Sentence counts

Use granularity: "sentence" if the checker needs sentence boundaries. Punctuation conventions differ by language, so describe the result as runtime segmentation, not as a grammar check or a universally tailored editorial sentence count. Unicode’s UAX #29 describes default segmentation rules and their profiles.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

UTF-8 byte size

Use TextEncoder when the constraint is encoded size—for example, a byte limit for storage or transmission—not when the interface asks how many characters a person sees.

const utf8Bytes = new TextEncoder().encode(text).length;

The encoding API also provides encodeInto(), whose result distinguishes UTF-16 code units read from the source from bytes written to the destination. This matters when writing into a bounded byte buffer. MDN’s TextEncoder reference documents UTF-8 encoding and encodeInto().

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

How can asynchronous analysis avoid stale results?

Each input revision should be treated as a separate analysis request. When newer text arrives, make prior work obsolete, and update visible results only if the completed request still corresponds to the current revision. Cancellation can avoid wasted work when the operation supports it, but a revision check is still useful for work that cannot be cancelled.

let currentRevision = 0;

async function analyzeCurrentText(text, setResult) {
  const revision = ++currentRevision;
  const result = await analyze(text);

  if (revision === currentRevision) {
    setResult(result);
  }
}

In a UI, clear or visibly mark prior output while a new analysis is pending if leaving it beside changed text could mislead. That is an interface choice based on the risk of showing a result for the wrong input, rather than a behavior required by a particular API.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

React effect pattern

React’s official useEffect guidance demonstrates an ignore flag set during cleanup to prevent out-of-order network responses from updating state. For example:

useEffect(() => {
  let ignore = false;

  analyze(text).then(result => {
    if (!ignore) setResult(result);
  });

  return () => {
    ignore = true;
  };
}, [text]);

The cleanup invalidates that effect’s result when the input changes or the component unmounts. Use the equivalent lifecycle mechanism in other frameworks. React’s useEffect reference explains the cleanup guard and why responses may arrive in a different order than requests.

What should you test before shipping?

Check that the displayed unit, source range, and latest-result behavior agree on inputs that expose the differences between counting models:

  • ASCII text, where code units, code points, and grapheme clusters generally align.
  • A supplementary-plane emoji such as a fox, which occupies two UTF-16 code units.
  • A base character plus a combining mark, which may be one grapheme cluster made from multiple code points.
  • A joined emoji sequence, which can appear as one cluster but contain multiple code points and code units.
  • Text in a language whose words are not separated by spaces, to verify locale-sensitive word segmentation.
  • Rapid successive edits with deliberately varied analysis completion times, to check that an older result never replaces the latest one.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More from the Wire

  1. World desk4 min
    How to Spot an AI Voice Scam Before Sending MoneyDon’t rely on how a caller sounds. Pause, call back through a known number, and verify the emergency with another trusted person before sending money.
  2. Mountain View desk4 min
    Google’s SynthID Detector: How to Check AI-Generated Images, Video and AudioGoogle’s SynthID Detector looks for an embedded watermark in supported images, video and audio. Here is what its results do—and do not—show.
  3. Redmond desk20 min
    How to create a link to File or Folder in Windows 11Windows 11 gives you several ways to point to a file or folder without moving or duplicating it. You can create a desktop shortcut,…
Recommended PC Tool
Recommended PC Tool
Windows Errors? Fix Them Before They SpreadFree repair scan
Crashes, No Sound, or Screen Glitches?Free driver scan

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.