# Reasoning

> Collapsible thinking trace: a shimmering “Thinking… 12s” label while active that settles into “Thought for 14s”, revealing muted reasoning text.

- **Slug:** `reasoning` · **Category:** agent · **Status:** beta
- **Import:** `import { Reasoning } from "@kura-ui/react/reasoning";`
- **Docs:** https://kuro.abstergo.fyi/components/reasoning
- **Registry item:** https://kuro.abstergo.fyi/r/reasoning.json
- **Tags:** reasoning, thinking, chain-of-thought, collapsible, trace, llm, timer

## Install

```bash
# shadcn registry (copies source into your project)
npx shadcn@latest add https://kuro.abstergo.fyi/r/reasoning.json
# Kura CLI (same, plus dependency resolution and --json output)
npx @kura-ui/cli add reasoning
# npm package
npm install @kura-ui/react
```

## When to use

- Show a reasoning model's chain of thought / thinking blocks (Claude extended thinking, o-series reasoning summaries) above the answer.
- Indicate that the model is thinking, with elapsed time, before the first answer token arrives.
- Keep a finished trace available but out of the way (collapsed to one line).

## When not to use

- A bare “Thinking…” label with no trace to reveal — use shimmer-text (or thinking-indicator).
- A sequence of discrete agent steps with states — use agent-timeline.
- A single tool invocation with arguments and results — use tool-call.

## Props

| Prop | Type | Default | Required | Description |
| --- | --- | --- | --- | --- |
| `active` | `boolean` | `false` | no | The model is still thinking: the label shimmers and an elapsed timer ticks. Turning it false settles the label and (with autoCollapse) collapses the trace. |
| `duration` | `number` | — | no | Seconds spent thinking, if you know it (e.g. stored with the message). Otherwise measured while `active`. |
| `open` | `boolean` | — | no | Expanded (controlled). |
| `defaultOpen` | `boolean` | — | no | Initial expanded state when uncontrolled. Defaults to `active`: open while thinking, closed for finished traces. |
| `onOpenChange` | `(open: boolean) => void` | — | no | Called when the trace is expanded/collapsed (by the reader or by auto-collapse). |
| `autoCollapse` | `boolean` | `true` | no | Collapse when `active` turns false — skipped if the reader toggled it while it was active. |
| `formatLabel` | `(state: { active: boolean; seconds: number }) => React.ReactNode` | — | no | Custom label (i18n, “Planning…”). Default “Thinking… 12s” / “Thought for 14s” / “Thought for a moment”. |

Native attributes of the root element, `className` and `ref` are passed through.

## Anatomy

- **Reasoning** (`data-slot="reasoning"`) — Root. `data-status="active" | "done"`, `data-state="open" | "closed"`.
- **Trigger** (`data-slot="reasoning-trigger"`) — Button with the label and a chevron that rotates 90° when open (aria-expanded, aria-controls).
- **Label** (`data-slot="reasoning-label"`) — ShimmerText; shimmers while active, muted when done. Seconds are tabular.
- **Content** (`data-slot="reasoning-content"`) — Muted, pre-wrapped trace behind a left hairline. Animates grid-template-rows 0fr↔1fr (interruptible) and is inert when closed.

## Accessibility

- The trigger is a real button with aria-expanded/aria-controls.
- A visually hidden status region says “Thinking” and then “Thought for 14s” — the ticking seconds are never announced.
- When collapsed, the trace is `inert`: out of the tab order and the accessibility tree.
- Reduced motion: The shimmer becomes static muted text, the chevron snaps, and the trace only fades (no height travel or blur).

| Keys | Action |
| --- | --- |
| Enter / Space | Expand or collapse the trace |

## Notes for agents

- Keep the component mounted from the first thinking token to the end; flip `active` instead of swapping components, so the timer and collapse work.
- Persist the measured duration with the message and pass it as `duration` when re-rendering history.
- Put a StreamingText (caret={false}) inside for a live trace; plain text or markdown for a stored one.

## Examples

### Finished trace (minimal)

A collapsed “Thought for 14s” trace.

```tsx
import { Reasoning } from "@kura-ui/react/reasoning";

const TRACE =
  "The user wants the like button to feel instant. The lag comes from waiting on the server round-trip before re-rendering.\n\nOptions: optimistic local state with manual rollback, or useOptimistic inside a transition. useOptimistic handles the rollback automatically if the action throws, and keeps the UI consistent with concurrent updates.\n\nI should recommend useOptimistic and show the minimal pattern, then mention the error case.";

export function ReasoningMinimal() {
  return (
    <Reasoning duration={14} className="w-full max-w-lg">
      {TRACE}
    </Reasoning>
  );
}
```

### Live thinking that auto-collapses (full)

Streams the trace with StreamingText while active (controlled open), then collapses to “Thought for Ns”.

```tsx
"use client";

import { RotateCcw } from "lucide-react";
import * as React from "react";
import { Button } from "@kura-ui/react/button";
import { StreamingText } from "@kura-ui/react/streaming-text";
import { Reasoning } from "@kura-ui/react/reasoning";

const TRACE =
  "The user wants the like button to feel instant. The lag comes from waiting on the server round-trip before re-rendering.\n\nOptions: optimistic local state with manual rollback, or useOptimistic inside a transition. useOptimistic handles the rollback automatically if the action throws, and keeps the UI consistent with concurrent updates.\n\nI should recommend useOptimistic and show the minimal pattern, then mention the error case.";

async function* thoughts(text: string): AsyncGenerator<string> {
  const words = text.split(/(?<=\s)/);
  for (let i = 0; i < words.length; i += 2) {
    yield words.slice(i, i + 2).join("");
    await new Promise((r) => setTimeout(r, 70 + Math.random() * 60));
  }
}

export function ReasoningFull() {
  const [run, setRun] = React.useState(0);
  const [active, setActive] = React.useState(true);
  const [open, setOpen] = React.useState(true);
  const [stream, setStream] = React.useState(() => thoughts(TRACE));

  const replay = () => {
    setRun((r) => r + 1);
    setStream(thoughts(TRACE));
    setActive(true);
    setOpen(true);
  };

  return (
    <div className="flex w-full max-w-lg flex-col items-start gap-4">
      <Reasoning
        key={run}
        active={active}
        open={open}
        onOpenChange={setOpen}
        autoCollapse
        className="w-full"
      >
        <StreamingText stream={stream} caret={false} onComplete={() => setActive(false)} />
      </Reasoning>
      <Button variant="secondary" size="sm" onClick={replay}>
        <RotateCcw /> Think again
      </Button>
    </div>
  );
}
```

### Above an assistant answer (composition)

Inside a Message, with a custom label.

```tsx
import * as React from "react";
import { Message, MessageAvatar } from "@kura-ui/react/message";
import { Reasoning } from "@kura-ui/react/reasoning";

const TRACE =
  "The user wants the like button to feel instant. The lag comes from waiting on the server round-trip before re-rendering.\n\nOptions: optimistic local state with manual rollback, or useOptimistic inside a transition. useOptimistic handles the rollback automatically if the action throws, and keeps the UI consistent with concurrent updates.\n\nI should recommend useOptimistic and show the minimal pattern, then mention the error case.";

export function ReasoningInMessage() {
  return (
    <Message
      from="assistant"
      name="Kura"
      avatar={<MessageAvatar name="Kura" />}
      className="max-w-xl"
    >
      <Reasoning
        duration={83}
        formatLabel={({ active, seconds }) =>
          active ? "Planning…" : `Planned for ${Math.round(seconds / 60)} min`
        }
      >
        {TRACE}
      </Reasoning>
      <p>
        Use <code>useOptimistic</code>: render the new count immediately, then let React reconcile
        when the server action resolves. If it throws, the optimistic value is dropped for you.
      </p>
    </Message>
  );
}
```

## Dependencies

- npm: `lucide-react`
- Kura registry items: `kura-theme`, `shimmer-text`, `use-controllable-state`, `utils`
- Related: `shimmer-text`, `thinking-indicator`, `message`, `streaming-text`, `agent-timeline`
