FindableChat
A complete AI chat widget with streaming responses, citations, and follow-up suggestions.
Live Chat Demo
Try: “What ventilation system is installed?”Try: “Tell me about fire safety measures”Try: “Show the electrical distribution”
Building Assistant
Full Example
<FindableChat
buildingId="bld-789"
placeholder="Ask about your building..."
title="Ask Findable"
language="en"
reasoningEffort="medium"
showCitations={true}
showFollowUps={true}
className="my-chat"
onError={(err) => console.error(err)}
onThreadCreated={(threadId) => saveThreadId(threadId)}
onCitationClick={(citation) => openDocument(citation.documentId)}
renderMessage={(msg) => <CustomMessage {...msg} />}
/>Props
| Prop | Type | Default | Description |
|---|---|---|---|
buildingId* | string | — | The building to ask questions about |
placeholder | string | 'Ask about your building...' | Input placeholder text |
title | string | 'Ask Findable' | Header title. Set to empty string to hide the header. |
language | 'en' | 'no' | 'nb' | 'nn' | — | Language for AI responses |
reasoningEffort | 'low' | 'medium' | 'high' | 'medium' | AI reasoning effort level |
showCitations | boolean | true | Show inline citation badges |
showFollowUps | boolean | true | Show follow-up suggestion chips |
className | string | — | Additional CSS class |
onError | (error: Error) => void | — | Error callback |
onThreadCreated | (threadId: string) => void | — | Called when a new conversation thread starts |
onCitationClick | (citation: AskCitation) => void | — | Called when a citation badge is clicked |
renderMessage | (msg: ChatMessage) => JSX.Element | — | Custom message renderer for full UI control |
Headless Hook: useChat
For full control over the UI, use the useChat hook directly:
import { useChat } from '@findable-ai/react';
function CustomChat() {
const {
messages,
status,
followUpSuggestions,
threadId,
sendMessage,
cancelStream,
clearChat,
} = useChat({ buildingId: 'bld-789' });
return (
<div>
{messages.map((msg) => (
<div key={msg.id} className={msg.role}>
{msg.content}
{msg.isStreaming && <span>...</span>}
</div>
))}
<input
onKeyDown={(e) => {
if (e.key === 'Enter') {
sendMessage(e.currentTarget.value);
e.currentTarget.value = '';
}
}}
/>
{status === 'streaming' && (
<button onClick={cancelStream}>Stop</button>
)}
</div>
);
}useChat Return Value
| Prop | Type | Default | Description |
|---|---|---|---|
messages | ChatMessage[] | — | All messages in the conversation |
status | ChatStatus | — | 'idle' | 'starting' | 'streaming' | 'finished' | 'failed' |
followUpSuggestions | string[] | — | Suggested follow-up questions |
threadId | string | null | — | Current conversation thread ID |
sendMessage | (query, docIds?) => void | — | Send a message |
cancelStream | () => void | — | Cancel an in-flight stream |
clearChat | () => void | — | Clear all messages and start a new thread |