ThinkingSteps
Expandable progress steps with visible and accessible status labels.
Installation
Import from the Reflow workspace package.
Basic
Open demoimport { ThinkingSteps, Icon, ThinkingStepDetails } from '@rtzll/reflow'
import { type Handle } from 'remix/component'
export function Example(handle: Handle) {
const steps = [
{ value: 'search', title: 'Searching sources', content: 'Found four relevant pages.', status: 'complete' as const },
{ value: 'analyze', title: 'Analyzing results', content: 'Comparing approaches.', status: 'active' as const },
]
const icon = <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M4 6h16M4 12h16M4 18h16" /></svg>
return () => (
<>
<ThinkingSteps label="Thinking" variant="timeline" defaultOpen={true} steps={[
{ value: 'search', title: 'Searched the web', icon: <Icon name="search" />, status: 'complete' },
{ value: 'sources', title: 'Read 3 sources', icon: <Icon name="globe" />, status: 'complete', content: (
<ThinkingStepDetails summary="Explored 6 files" details={files} />
) },
{ value: 'done', title: 'Done', icon: <Icon name="check" />, status: "complete" },
]} />
</>
)
}Minimal
A pair of steps with dot markers.
import { ThinkingSteps } from '@rtzll/reflow'
import { type Handle } from 'remix/component'
export function Example(handle: Handle) {
const steps = [
{ value: 'search', title: 'Searching sources', content: 'Found four relevant pages.', status: 'complete' as const },
{ value: 'analyze', title: 'Analyzing results', content: 'Comparing approaches.', status: 'active' as const },
]
return () => (
<>
<ThinkingSteps label="Thinking" variant="timeline" steps={[
{ value: 'command', title: 'Ran a command' },
{ value: 'done', title: 'Done' },
]} />
</>
)
}Streaming
Replay to watch each stage become active and complete.
import { ThinkingSteps } from '@rtzll/reflow'
import { type Handle } from 'remix/component'
export function Example(handle: Handle) {
const steps = [
{ value: 'search', title: 'Searching sources', content: 'Found four relevant pages.', status: 'complete' as const },
{ value: 'analyze', title: 'Analyzing results', content: 'Comparing approaches.', status: 'active' as const },
]
return () => (
<>
<ThinkingSteps label="Thinking" variant="timeline" steps={steps} />
</>
)
}Custom content
Use your own heading and body inside the same disclosure.
import { ThinkingSteps } from '@rtzll/reflow'
import { type Handle } from 'remix/component'
export function Example(handle: Handle) {
return () => (
<>
<ThinkingSteps label="Release checks" heading={<strong>Release checks · 2 passed</strong>} defaultOpen>
<ul>
<li>Build verified</li>
<li>Browser smoke test passed</li>
</ul>
</ThinkingSteps>
</>
)
}Streaming Text
Text arrives progressively inside the expanded step. Replay restarts the stream, and reduced motion reveals the complete text immediately.
import { ThinkingSteps } from '@rtzll/reflow'
import { type Handle } from 'remix/component'
export function Example(handle: Handle) {
const steps = [
{ value: 'search', title: 'Searching sources', content: 'Found four relevant pages.', status: 'complete' as const },
{ value: 'analyze', title: 'Analyzing results', content: 'Comparing approaches.', status: 'active' as const },
]
return () => (
<>
<ThinkingSteps label="Thinking" variant="timeline" steps={[{
value: "analysis",
title: "Analyzing the interface",
status: finished ? "complete" : "active",
content: <p aria-busy={!finished}>{textSoFar}</p>,
}]} />
</>
)
}With Images
Step content can contain native images, captions, and source links.
import { ThinkingSteps, ThinkingStepImage } from '@rtzll/reflow'
import { type Handle } from 'remix/component'
export function Example(handle: Handle) {
const steps = [
{ value: 'search', title: 'Searching sources', content: 'Found four relevant pages.', status: 'complete' as const },
{ value: 'analyze', title: 'Analyzing results', content: 'Comparing approaches.', status: 'active' as const },
]
return () => (
<>
<ThinkingSteps label="Vision Agent" variant="timeline" steps={[{
value: "preview",
title: "Reviewing the homepage",
content: <ThinkingStepImage src="/preview.png" alt="Homepage preview" caption="Homepage screenshot" />,
}]} />
</>
)
}Sources and details
Combine source badges, expandable notes, images, and step status in a single research flow.
import { ThinkingSteps, ThinkingStepSources, ThinkingStepSource, ThinkingStepDetails } from '@rtzll/reflow'
import { type Handle } from 'remix/component'
export function Example(handle: Handle) {
const steps = [
{ value: 'search', title: 'Searching sources', content: 'Found four relevant pages.', status: 'complete' as const },
{ value: 'analyze', title: 'Analyzing results', content: 'Comparing approaches.', status: 'active' as const },
]
return () => (
<>
<ThinkingSteps label="Research Agent" variant="timeline" steps={[{
value: "sources",
title: "Searching sources",
content: <>
<ThinkingStepSources>
<ThinkingStepSource href="/docs">Documentation</ThinkingStepSource>
<ThinkingStepSource delay={45}>Source</ThinkingStepSource>
</ThinkingStepSources>
<ThinkingStepDetails summary="Explored four files" details={files} />
</>,
}]} />
</>
)
}Full Example
import { ThinkingSteps } from '@rtzll/reflow'
import { type Handle } from 'remix/component'
export function Example(handle: Handle) {
const steps = [
{ value: 'search', title: 'Searching sources', content: 'Found four relevant pages.', status: 'complete' as const },
{ value: 'analyze', title: 'Analyzing results', content: 'Comparing approaches.', status: 'active' as const },
]
return () => (
<>
<ThinkingSteps label="Research Agent" variant="timeline" steps={steps} />
</>
)
}API Reference
Public props for the Reflow implementation. Native controls also accept their standard HTML attributes and Remix event mixes.
ThinkingSteps
| Prop | Type | Default | Description |
|---|---|---|---|
open | boolean | — | Controls the labeled disclosure from its parent. |
onOpenChange | (open: boolean) => void | — | Called when the disclosure requests a change. |
size | "default" | "compact" | — | Sets the size used by the group and its rows. |
steps | readonly ThinkingStep[] | — | Ordered steps with disclosure content and progress status. |
heading | RemixNode | — | Custom non-interactive heading for the disclosure. |
children | RemixNode | — | Custom disclosure body in place of the generated step list. |
defaultValue | string | — | Identifier of the step initially expanded in the accordion. |
label | string | — | Optional heading that wraps the step list in a disclosure. |
defaultOpen | boolean | — | Initial expansion state of the labeled outer disclosure. The timeline starts open by default. |
variant | "accordion" | "timeline" | — | Display steps as individual disclosures or as a continuous timeline. |
ThinkingStep
| Prop | Type | Default | Description |
|---|---|---|---|
value* | string | — | Unique identifier for the step and its disclosure. |
title* | string | — | Title of the step. |
description | string | — | Muted supporting text below the title in the timeline. |
content | RemixNode | — | Text, images, sources, or nested disclosure content. |
icon | RemixNode | — | Decorative icon in the timeline. Steps without icons show a dot. |
status | "active" | "pending" | "complete" | pending | Progress state. The timeline reveals steps when they become active or complete. |
ThinkingStepDetails
| Prop | Type | Default | Description |
|---|---|---|---|
summary* | string | — | Label for a nested disclosure. |
details | readonly string[] | — | List of file names, actions or other step details. |
children | RemixNode | — | Optional content inside the disclosure. |
ThinkingStepSources
| Prop | Type | Default | Description |
|---|---|---|---|
children* | RemixNode | — | Source badges arranged in a wrapping row. |
ThinkingStepSource
| Prop | Type | Default | Description |
|---|---|---|---|
children* | RemixNode | — | Source label or icon and label. |
href | string | — | Optional destination for a source link. |
delay | number | — | Entry delay in milliseconds. |
ThinkingStepImage
| Prop | Type | Default | Description |
|---|---|---|---|
src* | string | — | Image URL. |
alt | string | — | Image description for assistive technology. |
caption | string | — | Text below the image. |