Consider a fictional exhibition description limited to 240 visible characters. An older description contains 260. The author needs to shorten it deliberately, keeping the opening date and access instructions, instead of discovering that loading the editor removed the final sentence.
Preserve the imported description
For an existing Tiptap 3 editor, merge this extension into its normal extension list:
import { CharacterCount } from '@tiptap/extensions';
const visibleCharacters = new Intl.Segmenter('en', {
granularity: 'grapheme'
});
const descriptionLimit = CharacterCount.configure({
limit: 240,
autoTrim: false,
textCounter: text => [...visibleCharacters.segment(text)].length
});
Retain the application's document schema, content-loading code, and other extensions. This configures an extension; it does not create an editor. Verify Intl.Segmenter support in the environments your application supports before using this particular counter.
The current documentation describes autoTrim as preserving initial content when false. While that document remains over the limit, transactions that increase its size are blocked. Exercise the actual initial-load and later setContent paths in the installed release, especially if the application loads records after initialization.
Preservation changes the editing experience. An over-limit author may be able to delete a phrase but unable to add a new explanatory sentence first. Explain that condition beside the field, keep the original recoverable, and offer a deliberate shortening workflow.
Make the counting rule agree with the destination
These plain JavaScript expressions illustrate two different rules:
'A😀'.length; // 3 UTF-16 code units
[...new Intl.Segmenter('en', {
granularity: 'grapheme'
}).segment('A😀')].length; // 2 grapheme clusters
A grapheme counter suits a product rule expressed as visible characters. A receiving service may instead count code units, bytes, or another representation. Match that service's documented rule if it owns the limit. Neither count measures HTML payload bytes.
CharacterCount also offers mode: 'nodeSize', which measures document-node size rather than the ordinary text-size mode. That option changes the meaning of the total; it is not a general fix for emoji. Keep the default text-size mode for the grapheme policy above.
Read the same counter the extension enforces
With editor referring to this initialized editor:
function readDescriptionStatus(editor) {
const count = editor.storage.characterCount.characters();
return {
count,
remaining: 240 - count,
overLimit: count > 240
};
}
Update the application label after document updates. A negative remaining count should show the actual excess, such as “20 characters over,” instead of displaying zero and concealing the shortening required. The extension also exposes words(), but a word total does not determine compliance with a character limit.
For submission, capture the current document and recheck the chosen counting policy. The server must enforce its own trusted validation. A client extension cannot establish that a manually constructed request is acceptable. Displaying a word count covers a different choice: reporting a live statistic without relying on it as the final submission decision.
Check an old record and a new one
Use an ordinary short description, the 260-character imported description, and a description containing emoji or combining accents. Confirm that the old wording survives loading, deletion reduces the displayed count, and additions remain blocked while the document is over the configured limit. Then test the boundary with typing, paste, and the application's content-loading route.
For the exhibition, shorten the background sentence until the description fits. Reopen the saved record and read the opening date and access instructions in full. A smaller total is useful only if the author retained the information visitors need.