Skip to content

The one-liner renders the default composition. To change what the panel holds, render a root with the parts you want as its children. Every container with no children renders its own default, so you only spell out the level you change.

A root lays its children out in a row: Threads, the thread list in a column of its own, and Main, the column that holds the header, the conversation, the composer and the footer. Leave Threads out and Main takes the whole width.

The five panels below run in the example’s panel playground, examples/app/components/panel-playground.tsx.

<AssistedPanel mode="floating" threads models={models} />

This is the default composition. Every prop it takes is a root prop, so the rest of this page can keep it.

Render a root with Main and its three containers. Each renders its default, so this panel looks like the one-liner, opened in the bottom-left corner:

<AssistedPanel.Floating defaultPosition="bottom-left">
<AssistedPanel.Main>
<AssistedPanel.Main>
<AssistedPanel.Header />
<AssistedPanel.Conversation />
<AssistedPanel.Composer />
</AssistedPanel.Main>
</AssistedPanel.Main>
</AssistedPanel.Floating>

Leave a part out and it is gone. Reorder them and they move.

Threads is the thread list in a column before Main, for a panel wide enough to show both. The one-liner places it with threads="side":

<AssistedPanel.Embedded threads="side" models={models} />
<AssistedPanel.Main>

Spelled out, the same panel is a root with the two columns:

<AssistedPanel.Embedded models={models}>
<AssistedPanel.Threads />
<AssistedPanel.Main />
</AssistedPanel.Main>
</AssistedPanel.Embedded>

Give Header children and it renders exactly those. This sheet names the shop’s assistant, keeps the devtools and Close, and puts the thread select in a row of its own:

<AssistedPanel.Sheet>
<AssistedPanel.Main>
<AssistedPanel.Header>
<AssistedPanel.Icon>
<ShopLogo />
</AssistedPanel.Icon>
<AssistedPanel.Title>Shop assistant</AssistedPanel.Title>
<AssistedPanel.Devtools>
<AssistedDevtoolsPanel />
</AssistedPanel.Devtools>
<AssistedPanel.Close />
</AssistedPanel.Header>
<AssistedPanel.ThreadSelect />
<AssistedPanel.Conversation />
<AssistedPanel.Composer />
</AssistedPanel.Main>
</AssistedPanel.Sheet>

Any part may sit in any container under a root. This embedded panel shows the context window in its header and footer, swaps the conversation for settings, and keeps three parts of the composer:

<AssistedPanel.Embedded models={models}>
<AssistedPanel.Main>
<AssistedPanel.Header>
<AssistedPanel.Title />
<AssistedPanel.ContextDisplay />
<AssistedPanel.SettingsToggle />
</AssistedPanel.Header>
<AssistedPanel.SettingsPanel>
<AssistedPanel.ContextBreakdown />
<AssistedPanel.QuotaBanner
used={42}
limit={50}
unit="messages"
resetsIn="3h"
upgradeLabel="Upgrade"
/>
</AssistedPanel.SettingsPanel>
<AssistedPanel.Conversation>
<AssistedPanel.ConversationSearch />
<AssistedPanel.Messages components={{ AssistantMessage: Mine }} />
<AssistedPanel.ScrollToBottom />
</AssistedPanel.Conversation>
<AssistedPanel.Composer>
<AssistedPanel.Input />
<AssistedPanel.ModelSelector />
<AssistedPanel.Send />
</AssistedPanel.Composer>
<AssistedPanel.Footer>
<AssistedPanel.ContextDisplay variant="bar" className="flex-1" />
</AssistedPanel.Footer>
</AssistedPanel.Main>
</AssistedPanel.Embedded>

Messages takes components for what happens inside a message. Mine is the example’s own assistant message, built from MessagePrimitive:

function Mine() {
return (
<MessagePrimitive.Root className="rounded-lg border-s-2 border-teal-600 bg-teal-50 px-3 py-2 text-sm">
<MessagePrimitive.Parts />
</MessagePrimitive.Root>
);
}

A button part with asChild renders your element and keeps its behavior. This floating panel opens a thread drawer from the header with the host’s icon buttons, and opens only from a trigger the host places:

<>
<AssistedPanel.Floating launcher={false}>
<AssistedPanel.Main>
<AssistedPanel.Header>
<AssistedPanel.ThreadDrawerToggle asChild>
<IconButton icon={PanelLeft} />
</AssistedPanel.ThreadDrawerToggle>
<AssistedPanel.ThreadTitle />
<AssistedPanel.NewThread asChild>
<IconButton icon={Plus} />
</AssistedPanel.NewThread>
<AssistedPanel.Close />
</AssistedPanel.Header>
<AssistedPanel.ThreadDrawer />
<AssistedPanel.Conversation />
<AssistedPanel.Composer />
</AssistedPanel.Main>
</AssistedPanel.Floating>
<nav className="fixed start-1/2 bottom-6 z-40 -translate-x-1/2">
<AssistedPanel.Trigger asChild>
<Button>Help</Button>
</AssistedPanel.Trigger>
</nav>
</>

IconButton is the example’s own button. asChild hands it the part’s onClick, aria-label and aria-pressed.

  • Render parts inside a root. The root holds the panel’s state: open, the picked model, the drawer, the search and the settings.
  • A root is a row of Threads and Main. The header, the conversation, the composer and the footer go in Main.
  • A container with no children renders its default. With children, it renders only those.
  • Parts that have nothing to show render nothing: ModelSelector without models, Devtools without devtools, Footer without children.
  • Trigger is the one part that may sit outside a root, anywhere under AssistedProvider.

Each group’s parts, props and default are on its page under Components. To replace a part with your own, see Make your own part.