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 an 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 | (message: 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[] | — | Conversation messages. |
status | ChatStatus | — | 'idle' | 'starting' | 'streaming' | 'finished' | 'failed'. |
followUpSuggestions | string[] | — | Suggested follow-up questions. |
threadId | string | null | — | Current conversation thread ID. |
sendMessage | (query: string, documentIds?: string[]) => void | — | Send a message. |
cancelStream | () => void | — | Cancel an in-flight stream. |
clearChat | () => void | — | Clear messages and start a new thread. |