Skip to content
lab components / AI

Chat

This is a Lab component!

That means it doesn't satisfy our definition of done and may be changed or even deleted. For an exact status, please reach out to the Fancy team through the dev_fancy or ux_fancy channels.

import { Chat } from "@siteimprove/fancylab";

#Examples

#Basic Usage

Siteimprove AI

Welcome graphic

Let’s dive in to your Analytics data

If you're looking to explore visitor behavior, page performance, or traffic trends, you're in the right place.

Theme: agentic-ai-2025
const { adapter, messages, loading, resetChat } = useMockedAdapter(); const welcomeChoices: Choice[] = [ { label: "Where are users dropping off or struggling?", icon: ( <Icon> <IconAreaChart /> </Icon> ), }, { label: "Compare traffic and conversions to last month.", icon: ( <Icon> <IconSite /> </Icon> ), }, { label: "Show me unusual patterns in user behavior.", icon: ( <Icon> <IconCampaign /> </Icon> ), }, ]; return ( <Chat adapter={adapter} messages={messages} loading={loading} onClose={resetChat} welcomeHeading="Let’s dive in to your Analytics data" welcomeText="If you're looking to explore visitor behavior, page performance, or traffic trends, you're in the right place." welcomeChoices={welcomeChoices} /> );

#Usage with floating container

Theme: agentic-ai-2025
const { adapter, messages, loading } = useMockedAdapter(); const [shown, setShown] = useState<boolean>(false); return ( <> <Button onClick={() => setShown(!shown)} variant="primary"> Toggle Chat </Button> <ChatFloatingContainer shown={shown} onClose={() => setShown(false)}> <Chat adapter={adapter} messages={messages} loading={loading} onClose={() => setShown(false)} /> </ChatFloatingContainer> </> );

#Usage with HTTP adapter

Siteimprove AI

Welcome graphic

Welcome!

Theme: agentic-ai-2025
const { adapter, messagesState, loadingState } = useHttpChatAdapter({ baseUrl: "https://api.example.com", endpoints: { chat: "/chat", }, // optional customizations mapInbound: (data): ChatMessage => ({ id: data.id, role: "assistant", payload: data.payload ?? { type: "markdown", markdown: data.content ?? "" }, createdAt: data.createdAt ? new Date(data.createdAt) : new Date(), }), // mock fetch implementation for demonstration purposes fetchImpl: async (input: RequestInfo | URL, init?: RequestInit) => { console.log("HTTP Adapter fetch called with:", input, init); await sleep(1000); return new Response( JSON.stringify({ id: crypto.randomUUID(), content: "You sent: " + JSON.parse(init?.body as string).messages[0].content, }), { status: 200, headers: { "Content-Type": "application/json" } } ); }, }); const [messages, setMessages] = messagesState; const [loading] = loadingState; return ( <Chat adapter={adapter} messages={messages} loading={loading} onClose={() => setMessages([])} /> );

#Usage with custom adapter

Siteimprove AI

Welcome graphic

Welcome!

Theme: agentic-ai-2025
const [messages, setMessages] = useState<ChatMessage[]>([]); const [loading, setLoading] = useState<boolean>(false); const mockAdapter: ChatAdapter = { send: async (draft: ChatMessageDraft) => { if (draft.type === "prompt") { const promptMessage: ChatMessage = { id: crypto.randomUUID(), role: "user", payload: { type: "markdown", markdown: draft.content, }, createdAt: new Date(), }; setMessages((prevMessages) => [...prevMessages, promptMessage]); } setLoading(true); await sleep(1000); // Simulate network delay const responseMessage: ChatMessage = { id: crypto.randomUUID(), role: "assistant", payload: { type: "markdown", markdown: `You said: ${draft.content}`, }, createdAt: new Date(), }; setMessages((prevMessages) => [...prevMessages, responseMessage]); setLoading(false); }, }; return ( <Chat adapter={mockAdapter} messages={messages} loading={loading} onClose={() => console.log("Close chat")} /> );