Parts and the default composition
The panel comes ready-made: one line renders a header, a conversation and a composer that work with whatever the provider has. It is also made of parts, and the host can move, swap or leave out any of them. This page is about how those two fit.
Three levels of change
Section titled “Three levels of change”Most apps never go past the first level, and each level costs only what it changes.
- Use it.
<AssistedPanel mode="floating" />and its props: where it sits, what it is called, which threads and models it offers. - Rearrange it. Compose the parts by hand. Move the model picker to the header, drop the attachment button, put a context bar in a footer.
- Replace a part. Write your own component against
useAssisted,useAui,useAuiStateand the primitives, and drop it where the part went.
The panel’s source is not a level. There is nothing to copy into your app and keep in step with a release: a part you have not replaced updates with the package.
Containers fall back to their defaults
Section titled “Containers fall back to their defaults”A container part with no children renders its default. With children, it renders exactly those and nothing else. So <AssistedPanel.Header /> is the whole default header, while a Header with two children is those two.
This keeps a change local. To trim the composer, you write the composer’s children and leave <AssistedPanel.Header /> and <AssistedPanel.Conversation /> bare. You never restate a default to keep it.
The one-liner is the same rule one level up: AssistedPanel with a mode is a root with no children.
Any part anywhere
Section titled “Any part anywhere”Parts read the panel’s state from the root above them, not from their parent. A ModelSelector works in the header as well as in the composer, and a ContextDisplay in a footer as well as in the header. The groups on the Components pages say where a part sits by default, not where it must sit.
The root holds what several parts share: whether the panel is open, the picked model, the thread drawer, the conversation search, the settings. That is why a toggle and the thing it toggles may sit in different containers: SettingsToggle in the header swaps Conversation for SettingsPanel below it.
Trigger goes one step further. It reads only the provider’s open state, so it sits anywhere under AssistedProvider, in the host’s own toolbar.
Your buttons, our behavior
Section titled “Your buttons, our behavior”A button part takes asChild. With it, the part renders your element and hands it the click, the label and the pressed state. The host’s design system keeps its buttons, and the part keeps what the button does.
Only the button a part shows by default is replaced. Send with asChild replaces the send button, while the stop button that takes its place during a reply stays the package’s. A part’s behavior is more than one element, and the host replaces only the element it sees.
Where parts stop and slots begin
Section titled “Where parts stop and slots begin”Parts end where a single message begins. Inside Messages, the panel renders each message, each tool call, each day line, and the host changes them through components slots: AssistantMessage, UserMessage, DaySeparator, ToolFallback and the groups.
A message is not a place to arrange. It is a sequence of parts the model produced, text, reasoning, tool calls, rendered in order. A slot replaces how one kind of thing renders, which is the change a host wants there. Tool calls have their own seam on top: cards on AssistedProvider replaces the card of one tool everywhere.
Why the default looks the way it does
Section titled “Why the default looks the way it does”Several default panels were tried in the example shop. The chosen one is quiet:
- One header row. The icon, the title and the page line, the thread menu, a new-thread button, a search icon, the devtools when given, the window controls and Close.
- The model picker in the composer. It sits beside Send, where the choice applies, and only when the host passes
models. - Threads in a menu.
threadsalone opens a searchable menu with each thread’s preview. A select row and a drawer are the other styles. - Search behind an icon. The conversation search opens from the header and stays out of the way until then.
- Cards inline. A card stands in the conversation where the call happened, never folded into a group of tool calls.
- Settings and usage opt-in. The settings panel, the context display, the breakdown and the quota banner need data the host may not have, so the host places them.
Underneath is one rule: the default renders only what is real with the provider’s data. No model picker without models, no thread menu unless the host says its runtime has a thread list, no devtools button without devtools, no context ring without usage. A part with nothing to show renders nothing, so a bare default never shows a control that does nothing.