# Reasoning Indicator

> A React reasoning indicator for AI chat: a shimmering live timer while the model thinks, then a quiet "Thought for 12 s" row to expand.

- Type: React component (AI Kit)
- Access: Pro (the source needs a Wingo UI Pro plan, see https://wingo-ui.com/pricing)
- Version: 1.0.0
- Install: `npx wingo-ui@latest add reasoning-indicator`
- Docs: https://wingo-ui.com/components/reasoning-indicator

## Interaction

While the model thinks, the label shimmers, a timer counts up in tenths and then whole seconds, and the last two lines of the thought slide up under it. Tap or click the row or the peek (or Tab to the row and press Enter or Space) to open the full reasoning: it follows the newest thought while it streams, and scrolling up inside it lets go. When thinking ends the row settles on "Thought for 12 s" and closes again if it was open from the start; if you opened it yourself it stays open. On a phone the row keeps a 44px hit area and the open body never grows past 40% of the screen.

## Installation

```bash
npx wingo-ui@latest add reasoning-indicator
```

Pro item: run `npx wingo-ui@latest login` once with a Pro account. The CLI adds the files, their dependencies and a version record for updates.

With shadcn: `npx shadcn@latest add @wingo-ui/reasoning-indicator` (needs the @wingo-ui registry with your API key in components.json)

Files:

- `components/ui/reasoning-indicator.tsx`

npm dependencies: `npm i motion lucide-react`

Wingo UI dependencies (installed automatically):

- [Markdown](https://wingo-ui.com/components/markdown) ([Markdown](https://wingo-ui.com/components/markdown.md))
- [useStickToBottom](https://wingo-ui.com/components/use-stick-to-bottom) ([Markdown](https://wingo-ui.com/components/use-stick-to-bottom.md))
- [AI Chat](https://wingo-ui.com/components/ai-chat) ([Markdown](https://wingo-ui.com/components/ai-chat.md))
- [useControllableState](https://wingo-ui.com/components/use-controllable-state) ([Markdown](https://wingo-ui.com/components/use-controllable-state.md))
- [useLocale](https://wingo-ui.com/components/use-locale) ([Markdown](https://wingo-ui.com/components/use-locale.md))
- [Motion presets](https://wingo-ui.com/components/motion) ([Markdown](https://wingo-ui.com/components/motion.md))
- [Tones](https://wingo-ui.com/components/tones) ([Markdown](https://wingo-ui.com/components/tones.md))
- [UIProvider](https://wingo-ui.com/components/ui-config) ([Markdown](https://wingo-ui.com/components/ui-config.md))

## Usage

```tsx
import { ReasoningIndicator } from "@/components/ui/reasoning-indicator"

const labels = {
  thinking: "Working on it",
  thought: (duration: string) => `Worked for ${duration}`,
}

export function Thinking({ text, streaming, startedAt, endedAt }: { text: string; streaming: boolean; startedAt: number; endedAt?: number }) {
  return (
    <ReasoningIndicator
      content={text}
      streaming={streaming}
      startedAt={startedAt}
      endedAt={endedAt}
      labels={labels}
    />
  )
}
```

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `content?` | `string` | `""` | The reasoning as markdown. Empty with no children: the row is plain text, with nothing to open. |
| `children?` | `ReactNode` |  | Replaces the markdown body, e.g. a list of searched sites or steps. |
| `label?` | `ReactNode` | `""` | Replaces the default label in any state, e.g. "Searching the web" while a phase runs, or labels.stopped(duration) for a stopped message. Leave empty for thinking / thought. |
| `icon?` | `ReactNode` | `"<Brain />"` | The 16px icon before the label (a Globe for a web search). |
| `showIcon?` | `boolean` | `true` | Shows the icon before the label. |
| `showDuration?` | `boolean` | `true` | The live timer while thinking and the time in the done label. Off: the done label reads labels.thoughtBriefly. |
| `variant?` | `"ghost" \| "soft" \| "outline"` | `"ghost"` | ghost is a flush text row for messages; soft and outline wrap the row and the body in a rounded box for standalone use. |
| `size?` | `"sm" \| "md"` | `"md"` | The row is 28 / 32px tall with 13 / 14px text. The body is small markdown either way. |
| `tone?` | `"neutral" \| "brand" \| "success" \| "warning" \| "danger" \| "info"` | `"neutral"` | Colors the rail beside the body and the icon while thinking. Neutral uses the foreground. |
| `color?` | `string` | `""` | Any CSS color. Replaces the tone. |
| `shimmer?` | `boolean` | `true` | The moving highlight on the label and the icon pulse while thinking. Off (and under reduced motion) the label stays still and muted. |
| `maxHeight?` | `number` | `320` | Pixels of open body before it scrolls inside. On a phone it is also capped at 40dvh. 0 lets it grow. |
| `streaming?` | `boolean` | `false` | Thinking now: shimmer, live timer, the two-line peek and the body following new text. |
| `defaultOpen?` | `boolean` | `false` | Starts open (uncontrolled). |
| `open?` | `boolean` |  | Controls the disclosure. Pair with onOpenChange. |
| `onOpenChange?` | `(open: boolean) => void` |  | Called when the person toggles it or autoCollapse closes it. |
| `startedAt?` | `Date \| string \| number` |  | When thinking began. While streaming the timer counts from here; a second phase continues from it. |
| `endedAt?` | `Date \| string \| number` |  | When thinking ended. Before startedAt counts as 0. Without timing data the done label says labels.thoughtBriefly. |
| `durationMs?` | `number` | `0` | The final duration, when known; wins over startedAt / endedAt once done. Unset (Measured in the playground) uses the measured run. |
| `autoCollapse?` | `boolean` | `true` | When thinking ends, closes again if it was open when the stream started and nobody touched it. A row the person opened stays open. |
| `peek?` | `boolean` | `true` | Shows the last two lines of the thought under the label while it streams and the row is closed. |
| `locale?` | `string` | `""` | Formats the duration ("12 s", "2 min. 5 s"). Empty uses UIProvider, then the browser. |
| `now?` | `Date \| number` |  | Pins the clock for screenshots and tests. By default a shared ticker runs only while streaming. |
| `labels?` | `Partial<ReasoningIndicatorLabels>` |  | thinking "Thinking", thought (duration) => "Thought for 12 s", thoughtBriefly "Thought for a few seconds", stopped (duration) => "Stopped after 3 s", show "Show reasoning", hide "Hide reasoning" (sr-only on the trigger). Pass only the keys you translate. |
| `classNames?` | `Partial<Record<"trigger" \| "icon" \| "label" \| "duration" \| "peek" \| "content" \| "body", string>>` |  | Extra classes for the inner parts. |
| `className?` | `string` |  | Extra classes for the root, merged last. |

## Parts

### DEFAULT_REASONING_INDICATOR_LABELS (constant)

The English copy: thinking "Thinking", thought(duration) "Thought for 12 s", thoughtBriefly, stopped(duration), show "Show reasoning" and hide "Hide reasoning".

## Examples

- **In a message**: Above a streaming answer; autoCollapse folds it when the answer starts.
- **Named phases**: "Searching the web" with a Globe, then "Reading 3 sources", then done.
- **Standalone box**: variant soft with maxHeight 200 and a long reasoning that scrolls.
- **Stopped**: "Stopped after 3 s" for a message the person stopped.
- **Sizes and tone**: sm and md, the info tone, thinking and done.
- **Outline, brand**: The default labels with a duration over two minutes.

## See also

- [AI Chat](https://wingo-ui.com/components/ai-chat) ([Markdown](https://wingo-ui.com/components/ai-chat.md))
- [Chat Thread](https://wingo-ui.com/components/chat-thread) ([Markdown](https://wingo-ui.com/components/chat-thread.md))
- [Markdown](https://wingo-ui.com/components/markdown) ([Markdown](https://wingo-ui.com/components/markdown.md))
- [Collapsible](https://wingo-ui.com/components/collapsible) ([Markdown](https://wingo-ui.com/components/collapsible.md))
- [useStickToBottom](https://wingo-ui.com/components/use-stick-to-bottom) ([Markdown](https://wingo-ui.com/components/use-stick-to-bottom.md))

---

Canonical: https://wingo-ui.com/components/reasoning-indicator
More components: https://wingo-ui.com/components · Index for agents: https://wingo-ui.com/llms.txt
