Skip to main content

Character count

Show how many characters remain in a limited text field with a character count and live feedback.

These examples use the following JavaScript initializers:

Example

Breakpoints

Mobile

320px

Mobile Lg

480px

Tablet

640px

Desktop

1024px
This is an input with a character counter.
You can enter up to 20 characters
HTML
<div
class="max-w-xs"
data-scope="character-count" data-part="root"
>
<label
for="input-limit"
class="block"
>Text input</label>
<div>
<span
id="input-hint"
class="text-gray-50"
>This is an input with a character counter.</span>
</div>
<div data-scope="character-count" data-part="control" class="mt-2 relative">
<input
data-scope="character-count" data-part="input"
id="input-limit"
aria-describedby="input-hint character-count-hint"
class="p-2 w-full max-w-lg h-10 border border-gray-60 focus:outline-offset-0 focus:outline-4 focus:outline-blue-40v data-[invalid]:ring-4 data-[invalid]:ring-red-60v data-[invalid]:border-transparent data-[invalid]:outline-offset-4"
maxlength="20"
/>
</div>
<div class="pt-2">
<span
data-scope="character-count" data-part="description"
id="character-count-hint"
class="sr-only"
>You can enter up to 20 characters</span>
<span
data-scope="character-count" data-part="status"
aria-hidden="true"
class="text-gray-50 data-[invalid]:text-red-60v data-[invalid]:font-bold"
>20 characters allowed</span>
<span data-scope="character-count" data-part="sr-status"></span>
</div>
</div>

Component API

Initialization

import { characterCountInit } from '@uswds-tailwind/compat/character-count'
characterCountInit()

Initialize after the markup exists. CharacterCount is the corresponding instance class. See initialization and cleanup.

Anatomy

Use data-scope="character-count" data-part="root" on the wrapper. Required descendants are input, status, and sr-status. An ordinary <label> links to the input’s ID; there is no label part.

The optional control receives generated data-invalid. The optional description is a static, visually hidden limit hint. The runtime adds it to the input’s aria-describedby while preserving existing description IDs.

Part Attribute Description
root data-maxlength Authored fallback limit when the input has no maxlength. Generated from the effective limit after initialization.
input maxlength Authored initial limit. The runtime reads it and removes the native limit so users can enter excess characters. The maxLength option takes precedence over both markup limits.
input, control, status data-invalid Generated when the value exceeds a positive limit.
status aria-hidden="true" Generated. Hides the visual count from assistive technology.
sr-status aria-live Generated. Starts unset, then becomes polite; over-limit announcements use assertive.

Configuration

Option Description
maxLength Allowed character count; defaults to the input’s maxlength, then root data-maxlength, then 0 for no limit.
defaultValue Initial text; defaults to the input value.
value Controlled text; your application accepts edits by updating this option.
errorText Custom validity message; defaults to The content is too long.
statusLabel Initial count suffix; defaults to characters allowed.
ids Override root, control, input, description, status, or srStatus IDs. Existing input and description IDs are preserved by default.
inputDescriptionIds Existing hint IDs; defaults to the input’s aria-describedby.
onValueChange Receives { value: string } when the value changes or a controlled edit is requested. This is a JavaScript callback.

Methods and cleanup

api.setValue(value) requests a text update. api.value, api.count, api.maxLength, api.invalid, api.statusText, and api.srStatusText expose the current state.

characterCountInit() returns the initialized instances. Use CharacterCount.getOrCreateInstance(root, options) to configure an instance, and call destroy() before removing its markup. See initialization and cleanup.

Accessibility

Provide a visible label and a static limit hint. Keep both status elements. Count announcements follow a trailing debounce; over-limit text also sets native custom validity on the input.