@kelu/react gives you <KeluChat />, a finished chat panel, and useKeluChat, a hook for building your own. It needs react and react-dom 18 or later, depends on @kelu/sdk, and re-exports its types and KeluError.
@kelu/react is not on npm yet, so npm install @kelu/react fails today. Until it is published, embed the website widget or call the REST API directly.

Example

"use client";
import { KeluChat } from "@kelu/react";

export default function HelpPage() {
  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. In the Next.js App Router, use them from a file that starts with "use client".

KeluChat props

PropTypeDefaultDescription
knowledgeBaseIdstringrequiredThe knowledge base to answer from
clientKeystringrequiredPublic client key (kl_pk_…)
baseUrlstringhttps://app.kelu.devAPI base URL
placeholderstring"Ask a question…"Input placeholder
welcomeMessagestringnoneShown while the chat is empty
theme{ primaryColor?, borderRadius? }#343CED, 12pxAccent colour (buttons, links, citation numbers) and corner radius
classNamestringnoneClass name on the outer wrapper
groupIdsstring[]noneAnswer and search only from these source groups
searchEnabledbooleanfalseShow an Ask AI / Search toggle. Search lists matching pages without an answer
defaultMode"ask" | "search""ask"Which mode opens first when searchEnabled is on
searchPlaceholderstring"Search sources…"Placeholder in search mode
searchLimitnumber8Search results to show
labelsPartial<ChatLabels>EnglishReplace any of the search-mode strings, for example { modeAsk: "Ask", searchEmpty: "Nothing found." }
getCaptchaToken() => Promise<string>noneReturns a reCAPTCHA v3 token when the deployment requires one
In search mode, the search runs when the reader presses Enter or the search button, with at least 2 characters typed. ChatLabels keys: modeAsk, modeSearch, searchHint, searchEmpty, searchFailed, searching, fromTheDocs, askAiRow ({query} is replaced with the text typed) and askAiRowSub.
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)

import { useKeluChat } from "@kelu/react";

const { messages, ask, isStreaming, error, threadId, reset, sendFeedback, clearFeedback } =
  useKeluChat({ knowledgeBaseId: "YOUR_KNOWLEDGE_BASE_ID", clientKey: "kl_pk_..." });

Options

OptionTypeDefaultDescription
knowledgeBaseIdstringrequiredThe knowledge base to answer from
clientKeystringrequiredPublic client key (kl_pk_…)
baseUrlstringhttps://app.kelu.devAPI base URL
groupIdsstring[]noneAnswer only from these source groups
getCaptchaToken() => Promise<string>noneReturns a reCAPTCHA v3 token, sent with every ask()

Return value

PropertyTypeDescription
messagesChatMessage[]User and assistant messages in order. The assistant message fills in as the answer streams
ask(query: string) => Promise<void>Send a question on the current thread. Ignored while an answer is streaming or when the query is blank
isStreamingbooleantrue while an answer is streaming
errorKeluError | nullThe last failed answer or rating. Cleared by the next ask()
threadIdstring | undefinedThe conversation that later questions continue
reset() => voidStop any answer and start a new conversation
sendFeedback(messageId, rating: "up" | "down", comment?) => Promise<void>Rate an answer. A second rating replaces the first
clearFeedback(messageId) => Promise<void>Remove the rating
If a rating fails, sendFeedback and clearFeedback reject and set error to “Your rating could not be saved.”, so you can undo the button. Unmounting stops any answer in progress.

ChatMessage

FieldTypeDescription
idstringLocal id, for React keys
role"user" | "assistant"Who wrote it
contentstringMessage text
citationsCitation[]?On assistant messages: the sources the answer cited. Label each with its index, not its position
messageIdstring?Set on assistant messages once the answer is complete. Pass it to sendFeedback. Absent when the workspace keeps no chat data
Citation and the error codes on KeluError are in the @kelu/sdk reference.