The Chat Widget#
Warning
Project Neo is currently in Technical Preview. Features described in this section may change before general availability.
Your bundle can embed the same conversational GenAI experience the core Squirro application ships: a full chat panel that streams answers with citations, footnotes, thinking steps, and interrupt handling, backed by the GenAI service of the project. This page covers embedding the widget, the data source and its authentication prerequisite, and how to theme, localize, and gate it.
The widget is exported from @squirro/neo-core/chat. The bundle scaffold also installs a squirro-chat skill under .claude/skills/ with the full API reference and patterns, which is a useful companion when building with Claude Code.
What It Is#
Two components work together:
ChatProvider
Owns one instance-scoped bundle of chat state: the conversation, streaming, agents, and interrupts. Everything a chat surface needs is read from it.
ChatPanel
The embeddable widget itself, made up of the conversation thread and the composer. It reads its state from the surrounding provider, so you can drop it in without passing props.
Note
One ChatProvider corresponds to one conversation. Two ChatPanel components under the same provider share a single conversation. To run two independent chats on a page, mount two providers.
The panel deliberately does not include a conversation-list sidebar or any URL syncing. Those belong to the surface that mounts the provider.
The Data Source and Authentication#
The chat widget talks to /service/genai through a data source built with createChatDataSource(). Authentication works the same way as for the rest of the Squirro API, described on the Squirro API page:
During local development
The dev proxy injects a valid access token into every request.
In production
The Squirro backend authenticates the request via session cookies on the same origin.
Called with no arguments, createChatDataSource() reuses the authentication and project-scoping bindings registered by the host, which are the same ones useSquirroApi() uses. No manual token handling is required, and the same code path works in both environments.
Embedding the Widget#
ChatProvider needs a dataSource, project-scoped queryKeys, and a getProjectId accessor. A standalone bundle panel owns neither a conversation-list sidebar nor a host rejoin service, so the cacheUtils and observers props default to built-in no-ops and you do not pass them at all.
import {
ChatPanel,
ChatProvider,
createChatDataSource,
createGenaiQueryKeys,
} from '@squirro/neo-core/chat';
import { useSquirroApi } from '@squirro/neo-core/api';
import { useCallback, useMemo } from 'react';
export default function ChatDashboard() {
const api = useSquirroApi();
const getProjectId = useCallback(() => api.projectId, [api]);
const dataSource = useMemo(() => createChatDataSource(), []);
const queryKeys = useMemo(() => createGenaiQueryKeys(getProjectId), [getProjectId]);
return (
<ChatProvider
dataSource={dataSource}
queryKeys={queryKeys}
getProjectId={getProjectId}
>
<div className="h-full min-h-0">
<ChatPanel welcomeContent={<p className="text-muted-foreground">Ask a question to start.</p>} />
</div>
</ChatProvider>
);
}
Four points are worth calling out:
Memoize the data source and query keys
ChatProviderrebuilds its store bundle when the identity of those props changes, so keep the references stable.Size the container
Give the panel a sized container with
min-h-0so its internal scroll area lays out correctly inside a flex dashboard card.Query client
Dashboards render inside the React Query provider of the host, so the GenAI hooks resolve their client automatically. You do not need to supply one.
No sidebar wiring
cacheUtilsandobserversare optional and default to no-ops. Supply them only if you build your own conversation-list sidebar or rejoin service.
Theming, Strings, and Capabilities#
The widget uses the same theme tokens and typography as the rest of the application, so it picks up the active theme with no extra work. For more information, see the Styling and Components page.
Four optional ChatProvider props tailor the rest:
Prop |
Type |
Purpose |
|---|---|---|
|
|
Overrides user-facing text. Only the keys you pass change. The rest fall back to the English |
|
|
BCP-47 tag, for example |
|
|
Feature gates. |
|
|
Host hooks: |
For example, to localize a label and route document clicks into your own detail view:
<ChatProvider
dataSource={dataSource}
queryKeys={queryKeys}
getProjectId={getProjectId}
strings={{ codeBlockCopy: 'Copy code' }}
callbacks={{
onNotify: (kind, message) => toast[kind](message),
onNavigateToItem: (itemId) => openItemDetail(itemId),
}}
>
<ChatPanel />
</ChatProvider>
Note
Closing a side panel is a separate top-level prop, onCloseSidePanel, and is not part of callbacks.
Next Steps#
Topic |
Guide |
|---|---|
Wiring the panel into a dashboard entry |
|
Calling the Squirro API and how authentication is wired |
|
UI components, icons, and theme tokens |
|
Overriding text per language |
|
Live preview and prop table for the panel |
|
Full chat API reference |
The |