# Thinking indicator
> Thinking indicator with a pixel wave and a rolling seconds counter; when finished it reads "Thought for Ns" and expands to show the reasoning.
- React: `import { UiThinking } from "@/components/lumesec/ui-thinking"`
- Collection: Interaction (https://elements.lumesec.ai/components/interaction)
- Registry item: https://elements.lumesec.ai/r/ui-thinking.json
- Page: https://elements.lumesec.ai/components/interaction/thinking



Live preview: https://elements.lumesec.ai/view/ui-thinking

Demo source:

```tsx
"use client";

import * as React from "react";

import { UiThinking, type UiThinkingHandle } from "@/components/lumesec/ui-thinking";

const ACTION =
  "h-[30px] rounded-lg border border-border bg-card px-[11px] text-[12.5px] font-medium text-foreground hover:border-[color-mix(in_srgb,var(--lumesec)_50%,var(--border))]";

export default function UiThinkingDemo() {
  const thinking = React.useRef<UiThinkingHandle>(null);

  // the model "finishes" on its own after 6.4 s
  React.useEffect(() => {
    const timer = window.setTimeout(() => thinking.current?.finish(), 6400);
    return () => window.clearTimeout(timer);
  }, []);

  return (
    <div className="grid w-full max-w-[400px] gap-4">
      <div className="rounded-[14px] border border-border bg-card px-5 py-[18px] shadow-[0_14px_34px_-20px_rgb(0_0_0/0.4)]">
        <UiThinking ref={thinking}>
          Effort levels change how long the model reasons. Ultracode is different: it sends xhigh and lets the model
          plan multi-agent workflows on its own, so the slider should give it its own state rather than another grey
          step.
        </UiThinking>
      </div>
      <div className="flex flex-wrap justify-center gap-1.5">
        <button type="button" className={ACTION} onClick={() => thinking.current?.finish()}>
          Finish
        </button>
        <button type="button" className={ACTION} onClick={() => thinking.current?.restart()}>
          Restart
        </button>
      </div>
    </div>
  );
}
```

## Playground

Change a prop and the component re-renders. Props marked remounts set an initial value, so the component starts over.

## Installation

```bash
npx shadcn@latest add @lumesec/ui-thinking
```

First time with the @lumesec registry? Register it once, or install by URL:

```bash
npx shadcn@latest registry add @lumesec=https://elements.lumesec.ai/r/{name}.json
npx shadcn@latest add https://elements.lumesec.ai/r/ui-thinking.json
```

## Usage

React:

```tsx
"use client";

import * as React from "react";

import { UiThinking } from "@/components/lumesec/ui-thinking";

export function Example() {
  const [done, setDone] = React.useState(false);
  React.useEffect(() => {
    const timer = window.setTimeout(() => setDone(true), 4000);
    return () => window.clearTimeout(timer);
  }, []);
  return (
    <UiThinking done={done} onDone={(seconds) => console.log(`thought for ${seconds}s`)}>
      The reasoning, shown once thinking is done.
    </UiThinking>
  );
}
```

## Behaviour

A pixel wave and a shimmering label while the model works, with a seconds counter that rolls on every tick. When it finishes, the glyph draws a check and the label rolls to “Thought for 6s”, which opens the reasoning.

## API reference

### Props

Also accepts every prop of `<div>` (`React.ComponentProps<"div">`), spread onto the root element.

| Prop           | Type                        | Default | Description                                                                                                                                                                                                                                                                      |
| -------------- | --------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `done`         | `boolean`                   | —       | Whether thinking has finished (controlled). Use with `onDoneChange`. When it turns true, the glyph draws a check, the label rolls to "Thought for Ns" and the reasoning becomes expandable. Turning it false returns to thinking; the counter keeps running from the last start. |
| `defaultDone`  | `boolean`                   | `false` | Whether it starts finished, when uncontrolled. A component that starts done reads "Thought for 1s".                                                                                                                                                                              |
| `onDoneChange` | `(done: boolean) => void`   | —       | Called when `finish()` or `restart()` changes the state.                                                                                                                                                                                                                         |
| `onDone`       | `(seconds: number) => void` | —       | Called when the state changes to done, with the whole seconds elapsed (at least 1). Not called when the component mounts already done.                                                                                                                                           |
| `children`     | `React.ReactNode`           | —       | The reasoning, shown in a collapsible panel once done.                                                                                                                                                                                                                           |

### Ref

`ref` receives a `UiThinkingHandle` handle with these methods.

| Method            | Description                                                                          |
| ----------------- | ------------------------------------------------------------------------------------ |
| `finish(): void`  | Finishes thinking. Same as setting `done`.                                           |
| `restart(): void` | Resets the counter to 0 and returns to the thinking state, collapsing the reasoning. |

## Keyboard

| Keys          | Action                                               |
| ------------- | ---------------------------------------------------- |
| Enter / Space | On the label, once done: show or hide the reasoning. |

## Accessibility

* The label is a native `<button>` with `aria-expanded`. It is disabled while thinking and enabled once done; activating it shows or hides the reasoning.
* The collapsed reasoning is `inert`, so it is neither read nor reachable with Tab until it opens.
* The check icon and the pixel-wave canvas are `aria-hidden`.
* There is no live region, so the counter and the finished label are not announced.
* Reduced motion: the pixel wave holds at a fixed brightness, the label shimmer stops, and the label and counter swap text without rolling.

## Theming

Styled with Tailwind classes on your shadcn theme tokens, so light and dark follow your theme. The accent comes from `--lumesec`. See [Theming](/docs/theming).

This component reads `--lumesec`, `--lumesec-glint`, `--foreground`, `--muted` and `--muted-foreground`.

## Notes

* Seconds count from mount, or from `restart()`. The counter runs on a timer, so it stays correct while the component is off screen; the wave pauses there.
* The labels "Thinking" and "Thought for Ns" are built in English.


