Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Text

Pro

Typing Indicator Text for React

Who's typing, followed by three dots that rise and fall in a wave, as a chat shows while a message is written. For React with Base UI, when a conversation should show that a reply is on its way.

Framework

Typing Indicator Text says who's typing, "Alice is typing", followed by three dots that rise and brighten one after the other, the small wave every chat app uses for a message being written. The dots scale with the text and take its colour, so it sits in a chat's footer, a message bubble or an assistant's reply slot without any styling of its own.

Use it while someone, or an assistant, is writing a reply. For a loading placeholder that becomes text, use Skeleton Text; for a status that's live, Status Pulse Text. It's CSS only: each dot runs one keyframe animation the install adds to your stylesheet, delayed by its place, with no script and no animation library. With reduced motion the dots hold still.

Preview width

Installation

Part of Pro
Pro unlocks the code and one-line install of every Pro button and text effect, plus the MCP for your AI editor.
Have Pro already? Sign in to open it.Opens here as soon as you're Pro.

Usage

import {
  TypingIndicatorText,
} from "@/components/ui/typing-indicator-text";
<TypingIndicatorText text="Alice is typing" />

Demos

Bubble

hideText shows only the dots, here in a reply bubble; screen readers still hear the text. The dots are sized from the text, so a larger font gives larger dots.

Preview width

Status

Render it inside an element with role="status" that's always on the page, and screen readers announce "Sam is typing" when it appears. duration is one wave of the dots, 1200 ms by default; a longer one feels calmer.

Preview width

Accessibility

Screen readers hear the text once, as plain text, never the dots, which are hidden from them; with hideText the text is visually hidden but still read. To have the indicator announced when it appears, keep an element with role="status" on the page and render the indicator inside it when someone starts typing: a live region is only heard when its content changes, not when it arrives with the content already in it. The dots move without flashing, and with reduced motion they hold still.

FAQ

Why isn't the indicator a status region itself?

Because a live region that appears already holding its text is often not announced at all. Keep your own role="status" element on the page, empty when nobody's typing, and render the indicator inside it: the change is what screen readers hear.

How do I write the text in another language?

text is yours, so write it as your interface does: "Alice is typing", "Alice escribe", "Alice et Sam écrivent". The dots follow the text's direction on a right-to-left page.

Can I show only the dots?

Yes, with hideText: the text is visually hidden but still there for screen readers, so the dots never stand alone without a meaning.

How do I make the dots bigger or change their colour?

They're drawn from the text: a larger font gives larger dots, and they take the text's colour. To style them alone, target data-slot="typing-indicator-text-dot".

How do I change the speed?

duration is one wave in milliseconds, 1200 by default, and each dot starts an eighth of it after the one before, so the wave keeps its shape at any speed. You can also set --typing-indicator-text-duration in CSS.

What happens with reduced motion?

The dots hold still, fully lit, after the text.

API Reference

TypingIndicatorText

<span> data-slot="typing-indicator-text"

PropType
textstring

What it says before the dots, such as Alice is typing; screen readers hear it once.

duration?number

One wave of the dots, in milliseconds. @default 1200.

hideText?boolean

Shows only the dots; screen readers still hear the text. @default false.

Default
false
render?ReactElement | ((props: object, state: object) => ReactElement)

Replace the host element or compose it with another component.

Type
ReactElement | ((props: object, state: object) => ReactElement)
className?string

Merged with the part's classes.

CSS variableType
--typing-indicator-text-durationstring

One wave of the dots (1200ms).

All 97 text effects