Tabs
Split a reply into a few parallel views, each holding other components.
≈ 920 tokens per use (tool 351 · guidance 166 · typical payload 403)How token costs are estimated
Example: Tabs
Plan our long weekend in Lisbon.
When to use
- The reply splits into 2–5 parallel views of the same subject the user will switch between, like Overview, Packing and Budget for a trip.
When not to use
- The content is a sequence the user moves through. Use a guided flow.
- There's only one view, or the views are short enough to stack.
- Something the user must see would end up hidden in a later tab.
How it works
Each tab holds 1–3 other GenUI components, named by their catalog key and given their own props:
{
"tabs": [
{ "id": "overview", "label": "Overview", "children": [{ "component": "show_summary", "props": { "title": "…", "facts": [] } }] },
{ "id": "budget", "label": "Budget", "children": [{ "component": "build_calculator", "props": { "…": "…" } }] }
]
}- Layouts render through the Renderer. It passes them a
renderChildfunction, so children come from the same catalog as everything else. - Every panel stays mounted (just hidden), so a child's state, like ticked checklist steps, survives switching tabs.
- One level of nesting. A tab may hold columns, a grid or a split, and no deeper. A child's props may bind to a sibling's result.
- One tool call, one result per child. A layout is a single tool call. Each interactive child's result comes back as
{ child: "budget.0", result: { … } }, and it's restored the same way when the conversation reloads.
How the agent should use it
- The first (or
defaultTab) tab holds the main answer. - Keep labels to one or two words.
- Give each tab one clear purpose.
Installation
npx shadcn@latest add https://your-site.com/r/layout-tabs.json
Usage
Register it in your catalog next to the components it may contain:
import { LayoutTabs } from "@/components/genui/layout-tabs"
export const catalog = {
show_tabs: LayoutTabs,
show_summary: SummaryCard,
show_checklist: Checklist,
build_calculator: Calculator,
} satisfies GenUICatalogimport { tool } from "ai"
import { layoutTabsTool } from "@/components/genui/layout-tabs.schema"
const tools = {
[layoutTabsTool.name]: tool({
description: layoutTabsTool.description,
inputSchema: layoutTabsTool.inputSchema,
}),
}With LangGraph
Push it like any component, with ui.push({ name: "show_tabs", props: { toolCallId, ...args } }, { message }), and render it with the Renderer.
Schema
| Prop | Type | Notes |
|---|---|---|
tabs | { id, label, children }[] | 2–5 tabs. Labels up to 24 characters |
tabs[].children | { component, props, id? }[] | 1–3 components |
defaultTab | string? | The tab to open first |
Host props: status and className. renderChild is injected by the Renderer.