Skip to content

Reasoning

A collapsible chain of thought.

Use when a model exposes its reasoning separately from its answer and you want the reader to watch it happen without keeping it on screen afterwards.

Reasoning is the one part of a response a reader wants to watch while it happens and almost never wants to keep. So the element expands while streaming is set and collapses when it clears.

Until the reader touches it. Once they expand or collapse it themselves, the automatic behaviour stops for good — the same ownership rule createBottomFollower applies to scrolling, and for the same reason: an interface that keeps re-deciding something the reader has already decided is worse than one that never decided at all. Setting open from script counts as taking control too, since script is acting for a caller who has an opinion.

Quick Start

html
<r-reasoning label="Thinking"></r-reasoning>
ts
const reasoning = document.querySelector('r-reasoning');

reasoning.streaming = true; // expands
reasoning.content += delta; // grows while visible
reasoning.duration = 4200; // "4.2s" beside the label
reasoning.streaming = false; // collapses, unless the reader intervened

ranuts/stream already keeps reasoning-delta apart from text-delta, so a view can feed this straight from a snapshot:

ts
reasoning.content = snapshot.blocks
  .filter((block) => block.type === 'reasoning')
  .map((block) => block.text)
  .join('');
reasoning.streaming = !snapshot.done;

Details worth knowing

  • Sub-second durations render as nothing. A reader cares that it was fast, not that it was 340ms.
  • The label pulses while streaming, so a long silent think does not read as a stall. prefers-reduced-motion turns the animation off without removing the information.
  • The default slot replaces the rendered text, for a caller that wants <r-markdown> in the body instead of plain text.

API Reference

Properties

PropertyTypeDefaultDescription
contentstring''The reasoning text. Assigning repeatedly is the streaming path.
streamingbooleanfalseWhether reasoning is still arriving.
openbooleanfalseWhether the body is expanded.
labelstring'Reasoning'Summary text.
durationnumber | nullnullMilliseconds spent thinking. Hidden below one second.
sheetstring''CSS injected into the element's shadow DOM.

A duration that is not a finite, non-negative number reads back as null.

Slots

SlotDescription
(default)Replaces the rendered text with your own body content.

Parts

reasoning, summary, marker, label, meta, body, text.

Accessibility

The summary is a real <button type="button"> with aria-expanded, so it is reachable and operable from the keyboard without extra wiring.

See also

Released under the MIT License.