Make your own part
A part of your own is a component that reads the panel’s state and acts on it. Build it on what @super-assist/react exports, then render it where the part it replaces went.
Read and drive the panel
Section titled “Read and drive the panel”useAssisted gives the panel’s open state and the open page’s name:
import { useAssisted } from "@super-assist/react";
function PageBadge() { const { page } = useAssisted(); if (!page) return null; return <span className="rounded bg-muted px-1.5 text-xs">{page}</span>;}It also returns isOpen, open(message?) and close().
Read and drive the thread
Section titled “Read and drive the thread”useAuiState selects from the runtime’s state and re-renders when the selection changes. useAui returns the client that acts on it:
import { useAui, useAuiState } from "@super-assist/react";
function StartOver() { const aui = useAui(); const running = useAuiState((s) => s.thread.isRunning); return ( <button disabled={running} onClick={() => aui.threads.switchToNewThread()}> Start over </button> );}s.thread holds the open thread: messages, isRunning. s.threads holds the list: threadIds, threadItems, mainThreadId. aui.threads switches, starts and deletes threads. useAuiEvent subscribes to the runtime’s events.
Build on the primitives
Section titled “Build on the primitives”The primitives are unstyled components with the panel’s behavior: ComposerPrimitive, ThreadPrimitive, MessagePrimitive, ActionBarPrimitive, BranchPickerPrimitive, AttachmentPrimitive, ErrorPrimitive, SuggestionPrimitive, ThreadListPrimitive and ThreadListItemPrimitive. Style them with your own classes. A composer of your own:
import { ComposerPrimitive } from "@super-assist/react";
function PlainComposer() { return ( <ComposerPrimitive.Root className="flex gap-2 border-t p-2"> <ComposerPrimitive.Input className="flex-1 bg-transparent" placeholder="Ask the shop…" /> <ComposerPrimitive.Send className="rounded bg-primary px-3 text-primary-foreground"> Send </ComposerPrimitive.Send> </ComposerPrimitive.Root> );}A part you replace takes its behavior with it. PlainComposer has no slash commands, which belong to AssistedPanel.Input.
Drop it where the part went
Section titled “Drop it where the part went”Render the parts around it as before. Your components sit beside them:
<AssistedPanel.Floating> <AssistedPanel.Main> <AssistedPanel.Header> <AssistedPanel.Icon /> <AssistedPanel.Title /> <PageBadge /> <StartOver /> <AssistedPanel.Close /> </AssistedPanel.Header> <AssistedPanel.Conversation /> <PlainComposer /> </AssistedPanel.Main></AssistedPanel.Floating>Inside a root, your component sits inside the panel’s scoped styles and the runtime’s context. Outside one, under AssistedProvider, useAssisted, useAui and useAuiState still work.
Replace a message
Section titled “Replace a message”Parts end where a single message begins. To change how a message looks, pass a component to Messages’ components, 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> );}
<AssistedPanel.Conversation> <AssistedPanel.Messages components={{ AssistantMessage: Mine }} /></AssistedPanel.Conversation>;The slots are listed on Conversation. To change how a tool’s call shows, see Swap a card.
Reuse the panel’s pieces
Section titled “Reuse the panel’s pieces”The pieces the parts are built from are exported too, for a part that keeps their look: ThreadList, ModelSelector, EmptyState with EmptyStateGreeting, EmptyStateSuggestions and EmptyStateSuggestion, ConversationSearch, ThreadSearch, StoppedRun, SettingsPanel, ContextBreakdown, QuotaBanner, LauncherBubble, Suggestions, TooltipIconButton and ComposerTriggerPopover. They take their data as props. The react reference lists them.