<WebChat /> component, so you can keep useWebChat as the conversation and use any assistant-ui primitive, element, or part renderer you want on top.
The sections below are hints for common pieces (Thread, composer, reasoning, tools, custom UI). They are not the full assistant-ui surface.
The split
useWebChat holds the conversation: messages, sendMessage, respondToAction, sendAction, isRunning, and isLoading.
assistant-ui draws whatever you mount: Thread, ThreadList, composer, action bars, tool UIs, attachments, your own makeAssistantDataUI renderers, and the rest of the library. useExternalStoreRuntime is the glue. assistant-ui normally talks to its own backend. This runtime lets you feed it Novu instead.
Thread
Convert Novu messages into assistant-ui thread messages, then wrapThread in that runtime. Add a Thread UI from assistant-ui first (their Thread, or npx shadcn@latest add @assistant-ui/thread). Change the Thread import path to match your app.
Start with text. Add reasoning, tool-call, or data cases as you need them:
Composer
When the subscriber sends, assistant-ui callsonNew. Read the text and pass it to Novu. Do not start a second chat client.
isRunning or isLoading on the runtime (isDisabled) if you want the same behavior as the first-message form.
Reasoning
A Novuthinking part is the agent’s reasoning while the turn runs. Map it to assistant-ui reasoning so Thread can collapse it.
Tool UI
Atool part is a call that already ran. Map it to tool-call and let assistant-ui ToolFallback (or your own tool renderer) show the name, args, and result.
approval is the same tool-call shape, plus an approval object. When the subscriber picks Approve or Deny, onRespondToToolApproval must call respondToAction:
authorizeUrl in a custom part UI. Do not send that through respondToAction.
Custom UI
Novu cards andctx.emit payloads are not assistant-ui types. Send them through as named data items and register a renderer with makeAssistantDataUI.
NovuCardUI next to Thread (assistant-ui picks it up by name). Button clicks must call sendAction, not sendMessage. See Generative UI.
The same slot works for your own ctx.emit names. Register one makeAssistantDataUI per name you want to draw.
Full example
The Web Chat connect template is a complete reference: converter, Thread, cards, approvals, and a sidebar. Runnpx novu connect --channel web-chat to copy it into an app. The WebChat component in that output is scaffold code. It is not exported from @novu/react.