ToolzYard

Free online developer tools

Free • No Signup

Text Tools

Free online text tools that run in your browser. Count words, compare text, change case, create slugs, generate placeholder text and random strings, and test regular expressions. No sign-up and your text is processed locally.

Why string length and comparison lie to you

.length counts UTF-16 code units, not characters

JavaScript strings are UTF-16, so .length counts 16-bit code units rather than characters. Any code point above U+FFFF — most emoji and many CJK and historic scripts — is stored as a surrogate pair of two units, so "😀".length is 2, not 1. To count actual code points, iterate the string: [...str].length or Array.from(str).length use the string iterator, which yields whole code points.

Even code points are not what a user calls a character

What a person perceives as one character is a grapheme cluster, and a single grapheme can span several code points: an emoji with a skin-tone modifier, a flag built from two regional-indicator letters, or a family emoji joined by zero-width joiners. Splitting on code points can slice one of these in half. To count or reverse text the way a human would, use Intl.Segmenter with granularity: 'grapheme' — this is also why naively reversing a string can corrupt emoji.

Normalization, case, and slugs: the traps

"é" can be one code point or two

The character é has two valid encodings: a single precomposed code point (U+00E9, NFC form) or the letter e followed by a combining acute accent (U+0065 + U+0301, NFD form). They render identically but are different byte sequences, so a naive === can report two visually identical strings as unequal, and a slug generator or de-duplicator will miss the match. Normalize both sides to the same form first — str.normalize('NFC') is the usual choice. macOS is a common source of NFD strings: the older HFS+ filesystem normalized every filename to NFD on disk, and although APFS replaced that with a normalization-preserving scheme, NFD filenames created under HFS+ and by macOS tooling still circulate widely.

Case conversion is locale-dependent — the Turkish "i"

Uppercasing is not a simple lookup. In Turkish and Azerbaijani, the uppercase of i is the dotted İ and the lowercase of I is the dotless ı. A default toUpperCase() turns "istanbul" into "ISTANBUL", which is wrong for those locales and breaks case-insensitive matching. Use toLocaleUpperCase() / toLocaleLowerCase() when the locale matters, and for reliable case-insensitive comparison prefer Unicode case folding over ad-hoc lowercasing.

Generating a clean slug

To turn "Café Déjà Vu" into cafe-deja-vu, normalize to NFD and strip the combining marks — str.normalize('NFD').replace(/[\u0300-\u036f]/g, '') — before lowercasing and replacing spaces. Decomposing first is what lets you remove accents without a giant character-substitution table.

Frequently Asked Questions

Why does "😀".length return 2 in JavaScript?

JavaScript strings are UTF-16, and any character above U+FFFF (most emoji) is stored as a two-unit surrogate pair. .length counts code units, so it reports 2. Use [...str].length to count code points instead.

Why do two strings that look identical fail an equality check?

Accented and composed characters can be encoded more than one way — for example é as one code point (NFC) or as "e" plus a combining accent (NFD). The strings look the same but hold different bytes. Normalize both to the same form with str.normalize('NFC') before comparing.

How do I count real characters including emoji?

For code points, spread the string: [...str].length. For user-perceived characters like flag or skin-tone emoji, which are multiple code points joined together, use Intl.Segmenter with granularity: 'grapheme' and count the segments.

Why does uppercasing "i" break for Turkish text?

In Turkish and Azerbaijani the uppercase of i is İ and the lowercase of I is ı. A locale-neutral toUpperCase() produces the wrong letter, so use toLocaleUpperCase() / toLocaleLowerCase() when the language matters.

How does a diff tool decide what changed?

A text diff computes the shortest edit script — the fewest insertions and deletions that turn one text into the other — typically by finding a longest common subsequence. Git and most tools use Myers' algorithm. At the line level there is no true "modified" line; a changed line is simply one deletion plus one insertion.

Browse other tool categories