Hi @sabrina-stable-grove,
Fully agree with @erik-radiant-gorge — this is a UI concern and best solved on the frontend, not in the Document Model. Four model elements per field x 50 fields = 200 extra elements that inflate your model and create a maintenance headache for something that’s purely presentational. Kernel computations like Length() and FieldValueAsString() are designed for business rules and validation, not for driving UI decorations.
Assumed A12: 2024.06 (latest ext)
1. Custom WidgetMap approach (as @erik-radiant-gorge suggested)
Override TextLineStateless in a custom WidgetMap — you write this component once and it covers all your string fields:
const widgetMap: WidgetMap = {
...DefaultWidgetMap,
TextLineStateless(props) {
return <CharCounterTextLine {...props} />;
}
};
Inside CharCounterTextLine, use props.value for the current text and render the count via helperText, suffixText, or addonAfter — whichever fits your design. To get the max length, you can either hardcode it per annotation value or read it from the FormModel element via a custom React context (same pattern the official docs show for addonAfter customizations).
If the counter should only appear on specific fields rather than globally, add a Form Model annotation (e.g. "charCounter" with an optional value encoding the max length) and check for it in your component. Fields without the annotation simply fall through to the default widget — exactly the selective approach @erik-radiant-gorge mentioned.
2. Use Unicode-aware counting
One detail worth flagging: the kernel’s Length() counts Unicode code points, not user-perceived characters. The A12 docs explicitly note that a combined character like C̨̆ (code points 0x0043, 0x0328, 0x0306) returns Length = 3, even though users see one glyph. Emojis make this even worse.
Since you’re on the client side anyway, use Intl.Segmenter for accurate grapheme-cluster counting:
const count = [...new Intl.Segmenter().segment(value)].length;
This gives you “what the user actually sees” counts, which is what a character counter should reflect.
Note: If your maxLength validation in the Document Model uses kernel Length() (code-point semantics), your counter and your validation may disagree on edge cases with combined characters. Decide which semantics you need and align both sides, or document the difference for your users.
3. Purely modeled alternative
As @paul-tall-kernel pointed out, if your project strictly avoids frontend customization and relies entirely on modeling, you can reduce the overhead from four to two DM elements per field by moving the display logic into the Form Model via an ExpressionCell. I would only recommend this route if your project is committed to a purely model-driven approach where custom widget code is not an option. For all other cases, the WidgetMap approach from Section 1 gives you more flexibility, less model clutter, and proper Unicode handling.
Further reading: