Skip to main content
assistant-ui is a React library for chat UIs. Novu does not ship a <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 wrap Thread 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 calls onNew. Read the text and pass it to Novu. Do not start a second chat client.
Disable the composer with isRunning or isLoading on the runtime (isDisabled) if you want the same behavior as the first-message form.

Reasoning

A Novu thinking part is the agent’s reasoning while the turn runs. Map it to assistant-ui reasoning so Thread can collapse it.
See Thinking for the Novu fields.

Tool UI

A tool 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.
A pending approval is the same tool-call shape, plus an approval object. When the subscriber picks Approve or Deny, onRespondToToolApproval must call respondToAction:
See Tools for the Novu actions. For MCP connect, open authorizeUrl in a custom part UI. Do not send that through respondToAction.

Custom UI

Novu cards and ctx.emit payloads are not assistant-ui types. Send them through as named data items and register a renderer with makeAssistantDataUI.
Mount 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. Run npx 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.