Textarea maxlength: Emoji, UTF-16 Units and Line Breaks
Published · Updated
For a native textarea maxlength rule, measure the current textarea.value in UTF-16 code units. JavaScript value.length and the control’s textLength expose that count. A displayed emoji can consume more than one unit, and textarea line breaks are normalized before this count.
The number beside maxlength is a limit on those units, not a byte budget or a promise about the number of visible symbols. The maxlength reference specifies a nonnegative integer. For example, maxlength="2" provides two UTF-16 units.
Why emoji and accented text use different amounts
These examples contain exactly the strings shown. The decomposed accent uses e followed by U+0301; the programmer emoji uses woman, a zero-width joiner and laptop. Code points are shown separately because they also differ from units and visible characters.
| Text | UTF-16 units | Code points |
|---|---|---|
| A | 1 | 1 |
| é | 1 | 1 |
| e + combining acute | 2 | 2 |
| 😀 | 2 | 1 |
| 👩💻 | 5 | 3 |
The JavaScript length reference explains the unit distinction. A plain 😀 uses two UTF-16 units, while 👩💻 uses five. Counting with [...value].length changes the measurement to code points; it does not reproduce native maxlength.
Read the current field value
For an existing field, select the textarea and read its .value. Its HTML content describes initial text; it is a different scope from the value currently being edited. The HTML source and DOM text guide covers content elements rather than current form values.
The HTML Standard defines an API value with line endings normalized to LF. Assigning "A\r\n😀" therefore gives a value containing A, one LF and the emoji: four UTF-16 units. This measures the field value, not the bytes of a submitted request or original file.
Inspect normalization and a script-set overage
Run this example in a browser console. It creates a separate textarea in memory and leaves the page’s existing fields alone. The configured limit of three is only an illustrative test value.
const field = document.createElement("textarea");
field.maxLength = 3;
field.value = "A\r\n😀";
console.log(JSON.stringify(field.value)); // "A\n😀"
console.log(field.value.length); // 4
console.log(field.textLength); // 4
console.log(field.value.length > field.maxLength); // true
console.log(field.validity.tooLong); // false (set by script)
console.log(field.checkValidity()); // true
The textLength property reports the API-value length, so both length readings return four. The explicit comparison detects that four exceeds three.
Why tooLong can still be false
The value above was assigned by script. Native length constraints are checked for user-provided input; a programmatic assignment is not made invalid by maxlength, even when checkValidity() is called. Browsers generally prevent users from entering text beyond the native limit, so this diagnostic deliberately uses a scripted assignment.
Use the explicit length comparison when inspecting values loaded by code, and keep it separate from the rest of the form’s validation. The constraint-validation guide documents this distinction and the need to validate on the server too. If your application requires visible-character or byte limits, define and validate that rule explicitly on both sides.
Use the String Length guide to choose among code units, code points and other units. When checking a textarea limit, keep the selected value, normalization and unit together.