Streaming Skeleton
The placeholder a host shows while a tool call's name or props are still unknown.
Example: Streaming Skeleton
Raise the daily budget on the spring campaigns by 20%.
This isn't an agent tool. The agent never calls it: the Renderer or your host renders GenUIPlaceholder in the gap between "a tool call started" and "we know which component it is", then swaps in the real component.
When to use
- A tool call has started, but its name or props haven't arrived, so the real component can't render yet.
- You know roughly what's coming (a table, a list, a card) and want the message to keep its place.
When not to use
- The component is known. Render it with
status="streaming": every GenUI component has its own loading state. - The agent is still writing text. Show the text as it streams instead.
How the host should use it
- Replace it as soon as the tool name is known. The real component's streaming state takes over from there.
- Pick the shape from the tool name when you can, and fall back to
card. - Keep
linesclose to the expected size, so the layout doesn't jump when the component arrives. - Set
activityonly when you know the step (a search tool is running, the model is writing). Otherwise leave it out.
Shapes
| Shape | Surface | Default lines | Looks like |
|---|---|---|---|
card | Card | 3 | A title, one large value, label/value pairs and footer buttons |
table | Flush | 4 | A header row and data rows |
list | Card | 4 | A title and checkbox-style rows |
text | Flush | 3 | Paragraph lines, the last one shorter |
media | Card | 1 | A 16:9 block with caption lines |
Activity orb
Set activity and the placeholder shows a small monochrome orb with its label above the shapes, so the user sees what the agent is doing, not just that it's busy. Each activity has its own glyph:
activity | Orb variant | Default label | Motion |
|---|---|---|---|
thinking | pulse | Thinking… | A wave spreading from the centre of a 3×3 grid |
searching | orbit | Searching… | A comet chasing round a ring of dots |
writing | sweep | Writing… | A band moving left to right across the grid |
planning | globe | Planning… | Dots circling a flattened globe |
waiting | spark | Waiting… | A breathing ✦ with a soft echo |
label replaces the default text ("Searching the ledger…"). The orb ships as its own file, components/genui/orb.tsx, and you can use it on its own: <Orb variant="orbit" pill />. It takes variant, size (default 20), label, pill and announce (set it to false inside another status region). It's drawn with currentColor and animated with motion, so it needs no extra CSS. Adapted from Halaska UI's Orb (MIT).
Accessibility
- The root is
role="status"witharia-busy="true", and an sr-onlylabel("Generating…" by default) is announced politely. The skeleton shapes themselves are hidden from assistive tech. - With
activityset, the label is shown next to the orb instead of sr-only, and the root announces it. The orb glyph is hidden from assistive tech. - With reduced motion turned on, the pulse stops, the shapes stay still and the orb's dots stop moving.
Installation
npx shadcn@latest add https://your-site.com/r/streaming-skeleton.json
Usage
There's no tool to register. Render the placeholder for a tool part that has no name or no input yet:
import { GenUIPlaceholder } from "@/components/genui/streaming-skeleton"
const shapes = {
show_table: "table",
show_checklist: "list",
answer_with_sources: "text",
} as const
function ToolPart({ part }) {
if (part.state === "input-streaming" && part.input === undefined) {
return <GenUIPlaceholder shape={shapes[part.toolName] ?? "card"} />
}
return <GenUIRenderer call={fromAISDK(part)} catalog={catalog} />
}With LangGraph
Render it for a UI message whose name or props haven't arrived yet, then hand the message to the Renderer once they have.
Schema
| Prop | Type | Notes |
|---|---|---|
shape | card | table | list | text | media | Default card |
lines | number? | 1–12 rows or lines. Each shape has its own default |
label | string? | Status text: sr-only, or shown next to the orb when activity is set. Default "Generating…", or the activity's word |
activity | thinking | searching | writing | planning | waiting | Optional. Shows the activity orb |
Host props: className. It is always in the streaming state (data-status="streaming").
Source
Lifecycle States
What a generated component looks like when its data is old or incomplete, there's nothing to show, its offer has ended, a newer version replaced it, or the call failed.
Recovery Panel
When the agent can't finish what it proposed, it says what happened, what still worked, and offers concrete ways to continue.