Skip to content
Kura UI

Thinking Indicator

beta

Compact AI “thinking” indicators — dots, pulse, orbit, wave, or cycling status text — inside a polite live region.

@kura-ui/react/thinking-indicatorCopy page for agents.mdregistry.json

Thinking dots

minimal
Thinking

Every variant and size

dots, pulse, orbit and wave at sm/md/lg, cycling text, and a muted recolour.

full
smmdlgdotsThinking (dots, sm)Thinking (dots, md)Thinking (dots, lg)pulseThinking (pulse, sm)Thinking (pulse, md)Thinking (pulse, lg)orbitThinking (orbit, sm)Thinking (orbit, md)Thinking (orbit, lg)waveThinking (wave, sm)Thinking (wave, md)Thinking (wave, lg)
ResearchingThinking

Pending assistant message

Text variant inside a message bubble with a Stop Button.

composition
Assistant is working

Installation

npx shadcn@latest add https://kuro.abstergo.fyi/r/thinking-indicator.json

Copies the source into components/kura/thinking-indicator with its dependencies (kura-theme, shimmer-text, utils). Tokens come from the kura-theme item.

When to use it

Use it for

  • The assistant has received a prompt but not produced its first token yet.
  • A pending assistant message bubble or a placeholder row in a chat list.
  • Cycling short status lines while an agent works: “Searching…”, “Reading 4 files…” (variant text).
  • A tiny inline signal next to a label, tab or button that work is in progress.

Reach for something else when

  • Shimmering an existing label in place — use shimmer-text.
  • Network or file loading that is not model thinking — use spinner.
  • A whole container being worked on — use border-beam.
  • A voice agent's state — use agent-orb.
  • Streaming the answer itself — use streaming-text.

Props

PropTypeDefaultDescription
variant"dots" | "pulse" | "orbit" | "wave" | "text""dots"dots: three bouncing dots (staggered spring). pulse: a breathing dot with an expanding ring. orbit: two dots chasing round a track. wave: four equaliser bars. text: a pulse plus shimmering status text that cycles through `messages`.
size"sm" | "md" | "lg""md"Dot size 4 / 6 / 8 px; text 12 / 14 / 15 px.
labelstring"Thinking"Accessible status text. Announced once when the indicator mounts or the label changes — never on each animation cycle.
messagesreadonly string[]—Status lines for the text variant. Several lines cycle with a cross-fade (and only `label` is announced); a single line is shown and announced as is — update it as the agent progresses.
intervalnumber2400Milliseconds each message stays up when cycling (minimum 600).

All native attributes of the root element, className and ref pass through.

Anatomy

  1. ThinkingIndicatorInline root with role=status. `data-variant` and `data-size` mirror the props. Colour is currentColor (text-kura-accent by default).data-slot="thinking-indicator"
  2. ShapeThe animated glyph (aria-hidden).data-slot="thinking-indicator-shape"
  3. TextText variant only: all messages stacked in one grid cell (width = widest message, so no layout shift); the active one has data-active=true.data-slot="thinking-indicator-text"

Accessibility

  • Role: status
  • role=status + aria-live=polite: screen readers hear the label once, politely, not a stream of updates.
  • Shapes are aria-hidden; a visually hidden label carries the meaning.
  • When several messages cycle, the cycling text is aria-hidden and only `label` is announced, so the live region does not spam.
  • Colour is not the only signal: the text variant and the label describe the state.
  • Reduced motion: Loops stop. Dots show three static dots of rising opacity, the pulse is a still dot, orbit freezes with both dots visible, wave shows static bars of varied heights. Text still cycles but with an opacity cross-fade only (no blur, no rise, no shimmer).

Notes for agents

  • Recolour with a text class (`className="text-kura-fg-subtle"`); shapes use currentColor. Keep the jade default when it means “the model is working”.
  • Replace the indicator with the streamed message once the first token arrives; do not show both.
  • For real progress, pass one message and update it (`messages={[step]}`) — each change is announced. For ambient variety, pass several and let them cycle.
  • Keep messages short (≤ 5 words) and end with an ellipsis.