Research
ProThe progress surface for a deep-research run that takes minutes, not seconds: a plan you confirm, phases that fill, one live line of what the agent is reading, a growing pile of sources, and a single sentence when it lands.
When do solid-state batteries reach cost parity with lithium-ion?
0sSearching- PlanningDone
- SearchingIn progress
- ReadingNot started
- SynthesizingNot started
Installation#
Usage#
The preview is yours to explore. Its code comes with a Pro license, paid once and used in every project.
Have a license? Unlock it here
Anatomy#
Research is the long sibling of MessageReasoning: reasoning is a few seconds of thought folded into a turn, research is a run of several minutes with phases and a pile of sources that keeps growing. It borrows the Plan for the confirmation step and the reasoning's vocabulary for everything else: muted ink, one live line, a caret that turns.
import {
Research,
ResearchHeader,
ResearchIcon,
ResearchTitle,
ResearchElapsed,
ResearchStatus,
ResearchPlan,
ResearchPhases,
ResearchPhase,
ResearchActivity,
ResearchActivityItem,
ResearchFavicon,
ResearchFooter,
ResearchSources,
ResearchActions,
ResearchStage,
ResearchSummary,
ResearchSummaryTrigger,
ResearchSummaryContent,
} from "@/components/ui/research"
<Research>
<ResearchHeader>
<ResearchIcon />
<ResearchTitle />
<ResearchElapsed />
<ResearchStatus />
</ResearchHeader>
<ResearchPlan>{/* Plan parts */}</ResearchPlan>
<ResearchPhases>
<ResearchPhase />
</ResearchPhases>
<ResearchStage>
<ResearchActivity>
<ResearchActivityItem />
</ResearchActivity>
<ResearchSummary>
<ResearchSummaryTrigger />
<ResearchSummaryContent />
</ResearchSummary>
</ResearchStage>
<ResearchFooter>
<ResearchSources />
<ResearchActions />
</ResearchFooter>
</Research>The card is the only container. Phases, activity and the footer share the header's left edge and are set apart by whitespace, never by boxes or rules. Every part is droppable: a run with no plan step, a compact chip of just the sources, or a finished run that is only the summary line all work without rewiring.
Plan confirmation#
Before a long run spends minutes and money, it shows its angles. ResearchPlan is a plain Plan, so editing, adding and removing angles work exactly as they do there. Start the run, then use Update plan to steer it back.
When do solid-state batteries reach cost parity with lithium-ion?
Plan ready- Not startedCurrent cost per kWh for sulfide and oxide cells
- Not startedManufacturing yield and scrap rates on pilot lines
- Not startedLithium-ion price forecasts through 2030
- Not startedAnnounced volume dates from the six largest programs
- Not startedWhere the forecasts disagree, and why
5 angles · about 8 min
Phases#
Set phase on the root and each ResearchPhase works out its own state: earlier phases fill and settle in ink, the current one runs an indeterminate bar with its label swept, later ones wait on a faint track. Pass progress when you know how far a phase is, or status for phases of your own.
Planning: turning the question into angles
Planning- PlanningIn progress
- SearchingNot started
- ReadingNot started
- SynthesizingNot started
Searching: casting wide for candidate pages
Searching- PlanningDone
- SearchingIn progress
- ReadingNot started
- SynthesizingNot started
Reading: opening and extracting the best ones
Reading- PlanningDone
- SearchingDone
- ReadingIn progress
- SynthesizingNot started
Synthesizing: writing the answer and checking it
Synthesizing- PlanningDone
- SearchingDone
- ReadingDone
- SynthesizingIn progress
Done: every phase settled
Done- PlanningDone
- SearchingDone
- ReadingDone
- SynthesizingDone
Activity and sources#
The activity keeps four lines in view. A new line rises in from below and the stack climbs to make room; the oldest fade out under a top mask, so the eye stays on the newest line, which is the only one at full ink. Keep the array you render short (the last eight or so) and keep the full history for the summary. Favicons are letter tiles by default, so nothing is fetched from a third party unless you pass favicon. The sources chip counts up as pages land and opens the full list.
When do solid-state batteries reach cost parity with lithium-ion?
1m 36sReading- PlanningDone
- SearchingDone
- ReadingIn progress
- SynthesizingNot started
- Searching sulfide electrolyte yields
- Skimming iea.org
- Searching lithium-ion price forecasts
- Reading nature.com
- Reading arxiv.org
- Reading about.bnef.com
- Reading energy.gov
- Comparing three cost curves
Done#
A finished run collapses to one sentence: how many sources, how long. The phases stay filled as the record of what happened, and the summary opens to the whole activity log for anyone who wants to audit the trail.
When do solid-state batteries reach cost parity with lithium-ion?
6m 12sDone- PlanningDone
- SearchingDone
- ReadingDone
- SynthesizingDone
Stopped and failed#
Stopping keeps what was found. The phase it stopped in holds its partial fill in a quieter ink and the summary says how far it got, so the next step is a choice between resuming and writing up what is there. A failure reads the same way in the destructive tone, with the reason as the last line of the log.
When do solid-state batteries reach cost parity with lithium-ion?
2m 08sStopped- PlanningDone
- SearchingDone
- ReadingStopped
- SynthesizingNot started
When do solid-state batteries reach cost parity with lithium-ion?
1m 14sFailed- PlanningDone
- SearchingDone
- ReadingFailed
- SynthesizingNot started
Inside a conversation#
With variant="plain" the run sits in an assistant turn as part of the answer. Put it beside MessageContent inside a MessageBody, not inside the bubble, so the bubble's prose styles never reach its lists.
Battery cost parity
2m 23sReading- PlanningDone
- SearchingDone
- ReadingIn progress
- SynthesizingNot started
- Searching lithium-ion price forecasts
- Reading nature.com
- Reading arxiv.org
- Reading about.bnef.com
- Reading energy.gov
- Comparing three cost curves
- Reading reuters.com
- Reading idtechex.com
Density#
The card reads the density context. Compact tightens the block gaps, the padding and the activity line from 28 to 24px; the type and the radius stay put.
When do solid-state batteries reach cost parity with lithium-ion?
1m 21sReading- PlanningDone
- SearchingDone
- ReadingIn progress
- SynthesizingNot started
- Searching solid-state cost per kWh
- Searching sulfide electrolyte yields
- Skimming iea.org
- Searching lithium-ion price forecasts
- Reading nature.com
- Reading arxiv.org
- Reading about.bnef.com
When do solid-state batteries reach cost parity with lithium-ion?
1m 21sReading- PlanningDone
- SearchingDone
- ReadingIn progress
- SynthesizingNot started
- Searching solid-state cost per kWh
- Searching sulfide electrolyte yields
- Skimming iea.org
- Searching lithium-ion price forecasts
- Reading nature.com
- Reading arxiv.org
- Reading about.bnef.com
API reference#
Research
The card and the context every part reads. Forwards native <div> props. Every part accepts className, merged last, and every part is droppable.
| Prop | Type | Default | Description |
|---|---|---|---|
| status | "planning" | "running" | "done" | "stopped" | "error" | "running" | The run's state: drives ResearchStatus, the summary wording, the live ink on the newest activity line and aria-busy while running. |
| phase | "planning" | "searching" | "reading" | "synthesizing" | "done" | string | - | The phase the run is in. ResearchPhase rows before it settle as done, the matching one animates, the rest wait. |
| variant | "card" | "plain" | "card" | card is the standalone surface; plain drops the ring, shadow and gutter for a Chat turn or an AI Panel. |
| density | "comfortable" | "compact" | - | Block gaps, padding and the activity line height. Inherits the nearest DensityProvider. |
| asChild | boolean | false | Renders the child as the card via Radix Slot. |
| Data attribute | Description |
|---|---|
| data-status | The run's status. |
| data-phase | The current phase. |
ResearchHeader
The top row: icon, title, elapsed time, status. Forwards native <div> props.
ResearchIcon
The identity mark, Binoculars by default; pass children to swap it. aria-hidden.
ResearchTitle
The question being researched, clamped to two lines. Renders an <h3>.
| Prop | Type | Default | Description |
|---|---|---|---|
| asChild | boolean | false | Renders the child (another heading level) as the title. |
ResearchElapsed
How long the run has taken, in tabular-nums (42s, 6m 12s, 1h 04m). Ticks once a second while running and freezes when the run leaves running.
| Prop | Type | Default | Description |
|---|---|---|---|
| since | number | - | Epoch milliseconds the run started. Ticks from here while status is running. |
| seconds | number | - | A fixed value, for a finished run or a simulated clock. Wins over since. |
| format | (seconds: number) => React.ReactNode | formatResearchDuration | Custom wording. |
ResearchStatus
A chip worded and toned from status; while running it names the phase. Has role="status". Forwards Badge props.
| Prop | Type | Default | Description |
|---|---|---|---|
| variant | BadgeProps["variant"] | - | Overrides the tone. |
| children | React.ReactNode | - | Overrides the wording. |
ResearchPlan
The plan to confirm before the run starts. A Plan with variant="plain" and status="review" by default, so every Plan part (groups, editable steps, add step, footer) composes inside it. Takes PlanProps.
ResearchPhases
The phase rail, an <ol> with equal columns.
ResearchPhase
One phase: a thin Progress bar over its label. Its label defaults to the phase name.
| Prop | Type | Default | Description |
|---|---|---|---|
| value | string | - | The phase this row stands for. Compared with the root phase to derive its state. |
| status | "pending" | "active" | "done" | "stopped" | "failed" | - | Sets the state explicitly, for custom phases outside the built-in order. |
| progress | number | - | 0 to 100. A determinate fill for the active phase; without it the bar is indeterminate. |
| Data attribute | Description |
|---|---|
| data-status | The resolved phase state. |
ResearchActivity
The live stream. Bottom-anchored: the newest line rises in from below, older lines climb and fade under a top mask. Forwards native <div> props.
| Prop | Type | Default | Description |
|---|---|---|---|
| lines | number | 4 | Visible lines. 0 shows the whole history without a cap, for the summary. |
| Data attribute | Description |
|---|---|
| data-capped | Present when lines is above 0. |
| data-overflow | Present while older lines are clipped, which turns the top mask on. |
ResearchActivityItem
One line. The newest line of a running research is at full ink, the rest muted. Renders an <li>.
| Prop | Type | Default | Description |
|---|---|---|---|
| kind | "search" | "read" | "think" | - | Picks the glyph. Defaults to read with an href, think without. |
| href | string | - | The page being read; shows its favicon tile. |
| favicon | string | - | An image for the tile. Without it, the tile shows the domain's first letter. |
| icon | React.ReactNode | - | Replaces the glyph entirely. |
ResearchFavicon
The 16px source tile: a letter on an opaque muted chip, or src when you have one (it falls back to the letter on error).
| Prop | Type | Default | Description |
|---|---|---|---|
| href | string | - | The page; its domain gives the letter. |
| src | string | - | A favicon URL you host or proxy. |
| name | string | - | Overrides the letter's source. |
ResearchFooter
The bottom row: sources left, actions right. Wraps on narrow cards. Forwards native <div> props.
ResearchSources
The source pile: the latest favicons stacked, a rolling count, and a popover listing every source with its title and domain. Forwards <button> props to the trigger.
| Prop | Type | Default | Description |
|---|---|---|---|
| sources* | ResearchSourceData[] | - | { href, title?, favicon? }, oldest first. The newest lands at the end of the stack and the top of the list. |
| total | number | - | The headline count when it is larger than the list (pages skimmed but not opened). |
| max | number | 3 | How many favicons the stack shows. |
| open / defaultOpen / onOpenChange | PopoverProps | - | Controls the list popover. |
| children | (count: number) => React.ReactNode | - | Rewords the trigger. |
ResearchActions
Trailing slot for the run's buttons: Stop, Update plan, Resume. Forwards native <div> props.
ResearchSummary
The settled state. A Radix Collapsible.Root.
ResearchSummaryTrigger
The one line a finished run collapses to, worded from status, with a turning caret. Forwards Collapsible.Trigger props.
| Prop | Type | Default | Description |
|---|---|---|---|
| sources | number | - | Source count for the sentence. |
| seconds | number | - | Run length for the sentence. |
| children | React.ReactNode | - | Overrides the sentence. |
ResearchSummaryContent
What the summary opens to, usually the full activity history. Forwards Collapsible.Content props.
ResearchStage
Wraps the part that changes shape as the run moves on, usually the activity and the summary. When swapKey changes, its height settles from the old content to the new while the old fades out and the new fades in, so the phases and footer glide instead of jumping. Reduced motion keeps a short fade only. Forwards native <div> props.
| Prop | Type | Default | Description |
|---|---|---|---|
| swapKey* | React.Key | - | Changes when the content becomes a different state, e.g. "activity" then "summary". |