ChatSidenavComponent
ChatSidenavComponent is the conversation sidebar: the thread list, projects, search, and the new-chat action. Pair it with a runtime's thread store to turn a single chat surface into a multi-conversation app.
Selector: chat-sidenav
Import:
#When to Use It
Use <chat-sidenav> when users need more than one conversation โ history they can return to, rename, archive, or organize into projects.
It expects a backend that can actually enumerate and restore threads. @threadplane/langgraph provides that through LangGraphThreadsAdapter. AG-UI is event-stream-only and defines no thread-lookup endpoint, so on that adapter the thread list is app-owned state you maintain yourself.
Every <ng-content> slot on this component is named, and each one targets
a region inside the sidebar. There is no default slot, so a <chat> placed
between the tags is silently dropped โ you get a sidebar and an empty pane,
with no error.
Render the chat as a sibling and lay the two out yourself. This is the
opposite of <chat-sidebar>, which does
project your app content.
#Basic Usage
Selecting a conversation is a signal write. When provideAgent({ threadId: ACTIVE_THREAD }) is wired to the same signal, the adapter watches it and switches conversations โ the sidebar never talks to the agent directly. See Thread Routing for keeping that signal in sync with the URL.
#Inputs
| Input | Type | Default | Description |
|---|---|---|---|
mode | ChatSidenavMode | 'expanded' | 'expanded', 'collapsed' (icon rail), or 'drawer' (overlay). |
open | boolean | false | Drawer visibility. Supports two-way binding via openChange. |
threads | Thread[] | null | null | Active conversations, in display order. |
archivedThreads | Thread[] | null | null | Threads shown under the Archived disclosure. |
activeThreadId | string | null | null | Highlights the matching row. |
actions | ThreadActionAdapter | null | null | Per-row menu handlers. Omitted methods hide their menu items. |
projects | Project[] | null | null | Optional project grouping. |
selectedProjectId | string | null | null | Currently selected project. |
projectActions | ProjectActionAdapter | null | null | Project menu handlers. |
agent | Agent | AgentWithHistory | null | null | Powers the devtools panel and history search. |
debug | boolean | true | Shows the devtools launcher in the footer. |
#Outputs
| Output | Payload | Fires when |
|---|---|---|
newChat | void | The new-chat button is clicked. |
threadSelected | string | A thread row is chosen. |
searchOpened | void | The search affordance is activated. |
openChange | boolean | Drawer opens or closes. |
modeChange | ChatSidenavMode | The user collapses or expands the rail. |
projectSelected | string | A project is chosen. |
newProjectRequested | void | The new-project action is clicked. |
#The Thread contract
Two of these fields are documentation of intent, not behavior โ the component does not act on them for you:
statusis not auto-filtered. Pre-filter your list and pass archived rows through the separatearchivedThreadsinput.pinnedis not auto-sorted. The pin icon renders, but you sort pinned threads to the top yourself.
LangGraphThreadsAdapter already does both, which is why the example above passes threads() and archivedThreads() straight through.
#Row actions
The framework handles the confirmation dialog for delete, the inline editor for rename, and optimistic UI with rollback on rejection.
Optimistic overrides are cleared in a finally block. If an adapter method
resolves but the threads input still holds the old data, the row snaps back
to its previous state โ which reads as "rename didn't work."
#Drawer mode
On narrow viewports, switch mode to 'drawer' and pair the sidenav with <chat-sidenav-scrim> for the dismissable backdrop: