---
title: Character count (Vanilla) - USWDS + Tailwind
description: Show how many characters remain in a limited text field with a
  character count and live feedback.
---

# 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:

* [Character Count](https://v2.uswds-tailwind.com/docs/vanilla/javascript#character-count)

## Example

```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

```js
import { characterCountInit } from '@uswds-tailwind/compat/character-count'

characterCountInit()
```

Initialize after the markup exists. `CharacterCount` is the corresponding instance class. See [initialization and cleanup](https://v2.uswds-tailwind.com/docs/vanilla/javascript#instance-lifecycle).

### 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](https://v2.uswds-tailwind.com/docs/vanilla/javascript#instance-lifecycle).

### 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.
