@keluhq/react
v0.1.1
Published
React components and hooks for Kelu — RAG chat with citations over crawled documentation.
Readme
@keluhq/react
React components and hooks for Kelu — an AI knowledge assistant that answers questions with RAG-based chat and citations over your crawled documentation.
Full reference: https://docs.kelu.dev/sdks/react
Install
npm install @keluhq/react@keluhq/sdk comes with it as a dependency.
reactandreact-dom(>=18) are peer dependencies and must be installed in your project.
You need a knowledge base ID and a public client key (kl_pk_…). Open the knowledge base in the Kelu dashboard and go to Integrations: Client Keys creates the key, and Developer SDKs shows starter code with your knowledge base ID and a public key filled in. The ID is also the part of the dashboard address after /knowledge-bases/.
CAPTCHA
On app.kelu.dev, a public key must send a reCAPTCHA v3 token with every question, search and rating. KeluChat and useKeluChat get one for you in the browser, so there is nothing to set up. What it loads, and what to allow in a Content Security Policy, is in the @keluhq/sdk README. To mint tokens yourself, pass getCaptchaToken.
Quickstart
Inline chat panel
Drop the KeluChat component anywhere in your app for a fully-styled, zero-configuration chat panel:
import { KeluChat } from "@keluhq/react";
export default function DocsPage() {
return (
<KeluChat
knowledgeBaseId="YOUR_KNOWLEDGE_BASE_ID"
clientKey="kl_pk_..."
welcomeMessage="Hi! Ask me anything about our documentation."
theme={{ primaryColor: "#5b21b6" }}
/>
);
}Both exports use React hooks, and the package is marked "use client", so in the Next.js App Router you can render KeluChat from a server component. A function prop such as getCaptchaToken can only be passed from a client component.
Headless hook
Use useKeluChat to build your own UI while the hook handles all streaming, state management, and error handling. Each message's content is Markdown with [n] citation markers; render it with renderMarkdown from @keluhq/sdk (install it next to this package with npm install @keluhq/sdk). Its output escapes all text, so it is safe to insert:
"use client";
import { useState } from "react";
import { useKeluChat } from "@keluhq/react";
import { renderMarkdown } from "@keluhq/sdk";
function MyChat() {
const { messages, ask, isStreaming, error, reset } = useKeluChat({
knowledgeBaseId: "YOUR_KNOWLEDGE_BASE_ID",
clientKey: "kl_pk_...",
});
const [input, setInput] = useState("");
return (
<div>
<button onClick={reset}>Clear</button>
<ul>
{messages.map((m) => (
<li
key={m.id}
style={{ textAlign: m.role === "user" ? "right" : "left" }}
dangerouslySetInnerHTML={{ __html: renderMarkdown(m.content) }}
/>
))}
</ul>
{isStreaming && <p>Thinking…</p>}
{error && <p style={{ color: "red" }}>{error.message}</p>}
<input
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyDown={(e) => {
if (e.key === "Enter") {
void ask(input);
setInput("");
}
}}
/>
</div>
);
}Submitting feedback
Both the component and the hook support per-message thumbs up/down:
const { sendFeedback, clearFeedback } = useKeluChat({ knowledgeBaseId, clientKey });
// After the user clicks thumbs up on a message:
await sendFeedback(messageId, "up");
await sendFeedback(messageId, "down", "Wrong"); // replaces the earlier vote, with comment
await clearFeedback(messageId); // withdraws itmessageId is the server's id on an assistant ChatMessage. It is "" when
the workspace keeps no data (zero retention); KeluChat then shows no thumbs.
KeluChat props
| Prop | Type | Default | Description |
|-------------------|-----------------------|-----------------------|-----------------------------------------------------|
| knowledgeBaseId | string | required | Your Kelu knowledge base ID. |
| clientKey | string | required | Public client key (kl_pk_…). |
| baseUrl | string | https://app.kelu.dev | Override the API base URL. |
| placeholder | string | "Ask a question…" | Input placeholder text. |
| welcomeMessage | string | — | Greeting shown before the first message. |
| theme | KeluTheme | — | Visual customisation (see table below). |
| className | string | — | Extra class name(s) on the outer wrapper. |
| getCaptchaToken | () => Promise<string> | automatic | Your own reCAPTCHA v3 token source, replacing the automatic one (see CAPTCHA). |
| groupIds | string[] | — | Restrict answers and search to these source groups. |
| searchEnabled | boolean | false | Show the Ask / Search toggle. |
| defaultMode | "ask" \| "search" | "ask" | Mode the panel opens in when searchEnabled. |
| searchPlaceholder | string | "Search sources…" | Placeholder of the search field. |
| searchLimit | number | 8 | How many search results to request. |
| labels | Partial<ChatLabels> | — | Override the search-mode strings; unset keys stay English. |
KeluTheme
| Token | Type | Default | Description |
|-----------------|----------|-------------|---------------------------------------------------------|
| primaryColor | string | #343CED | Brand colour for buttons, accents, and citation badges. |
| borderRadius | string | 12px | Border radius applied to the panel and bubbles. |
ChatLabels
Keys you can pass in labels, for example labels={{ modeAsk: "Ask", searchEmpty: "Nothing found." }}:
| Key | Default |
|---|---|
| modeAsk | Ask AI |
| modeSearch | Search |
| searchHint | Type a query and press Enter to search. |
| searchEmpty | No matching pages. |
| searchFailed | Search is unavailable right now. |
| searching | Searching… |
| fromTheDocs | From the docs (heading over the results) |
| askAiRow | Ask AI: “{query}” ({query} is replaced with the text typed) |
| askAiRowSub | Press Enter for a full answer |
The header reads AI Assistant and the footer shows Powered by Kelu. Neither can be changed. For your own markup, use the hook.
useKeluChat options
| Option | Type | Description |
|-------------------|-----------------------|-------------------------------------------------|
| knowledgeBaseId | string | Your Kelu knowledge base ID. |
| clientKey | string | Public client key (kl_pk_…). |
| baseUrl | string | Override the API base URL. |
| getCaptchaToken | () => Promise<string> | Your own reCAPTCHA v3 token source for every question and rating, replacing the automatic one. See CAPTCHA. |
| groupIds | string[] | Restrict retrieval to these source groups. |
useKeluChat return value
| Property | Type | Description |
|-----------------|-----------------------------------------------------------|---------------------------------------------------------------|
| messages | ChatMessage[] | Ordered list of user and assistant messages. |
| ask | (query: string) => Promise<void> | Send a query; streams tokens into the last assistant message. |
| isStreaming | boolean | true while a response is being streamed. |
| error | KeluError \| null | Last error, if any. |
| threadId | string \| undefined | Active conversation thread ID. |
| reset | () => void | Clear messages, abort any in-flight stream, reset thread. |
| sendFeedback | (id: string, rating: "up" \| "down", comment?: string) => Promise<void> | Submit message feedback. |
| clearFeedback | (id: string) => Promise<void> | Withdraw the rating on a message. |
ChatMessage is { id, role: "user" | "assistant", content, citations?, messageId? }. content is Markdown. Label each citation with its index, not its position in the list. error is a KeluError; its codes are listed in the @keluhq/sdk README.
Changing knowledgeBaseId, clientKey or baseUrl starts a new conversation: any answer in progress stops, and messages, threadId and error are cleared.
VERSION (import { VERSION } from "@keluhq/react") is the installed version, for example "0.1.0".
License
MIT
