---
title: React Hooks
sidebar_position: 4
---

# React Hooks

`@elaras/react-chat` provides a `useChat()` hook and `ElarasProvider` for building chat UIs in React. It handles session management, streaming, and state internally so you can focus on your UI.

## Installation

```bash
npm install @elaras/react-chat
```

## Setup — ElarasProvider

Wrap your app (or just the part that needs the chatbot) with `ElarasProvider`:

```tsx
import {ElarasProvider} from '@elaras/react-chat';

function App() {
  return (
    <ElarasProvider chatbotKey="cbt_your_key_here">
      <YourApp />
    </ElarasProvider>
  );
}
```

### ElarasProvider props

| Prop | Type | Required | Description |
|---|---|---|---|
| `chatbotKey` | `string` | Yes | Your public chatbot key (`cbt_...`) |
| `apiUrl` | `string` | No | Override the API base URL (defaults to `https://api.elaras.ai/api`) |
| `children` | `ReactNode` | Yes | Your component tree |

## useChat()

Call `useChat()` inside any component that is a descendant of `ElarasProvider`:

```tsx
import {useChat} from '@elaras/react-chat';

function ChatWidget() {
  const {messages, send, status, isTyping, error, clearSession} = useChat();

  // ...
}
```

### Return values

| Value | Type | Description |
|---|---|---|
| `messages` | `Message[]` | All messages in the current session (user and assistant) |
| `send` | `(text: string) => void` | Send a message. Automatically appends the user message and streams the AI response |
| `status` | `ChatStatus` | Current state of the chat |
| `isTyping` | `boolean` | `true` while waiting for the first token (shows the typing indicator) |
| `error` | `string \| null` | Error message if `status === 'error'`, otherwise `null` |
| `clearSession` | `() => void` | Clear the session and start a fresh conversation |

### Message type

```ts
interface Message {
  id: string;              // Client-side UUID (for React key)
  role: 'user' | 'assistant';
  content: string;         // Full content (grows as tokens arrive during streaming)
  messageId?: number;      // Server-assigned ID (set when 'done' event arrives)
  createdAt: Date;
}
```

### ChatStatus type

```ts
type ChatStatus = 'idle' | 'typing' | 'streaming' | 'error';
```

| Status | Description |
|---|---|
| `idle` | No active request — ready to send |
| `typing` | Message sent, waiting for the first token from the AI |
| `streaming` | Receiving tokens — the last assistant message is growing |
| `error` | The last request failed — `error` contains the message |

**Tip:** Use `isTyping` (which is `status === 'typing'`) to show a typing indicator bubble before the first token arrives. Once `status === 'streaming'` the tokens are rendering in the last message directly.

## Full example

```tsx
import React, {useState, useRef, useEffect} from 'react';
import {ElarasProvider, useChat} from '@elaras/react-chat';

// Provider — put this at the top of your tree
export function ChatApp() {
  return (
    <ElarasProvider chatbotKey="cbt_your_key_here">
      <ChatUI />
    </ElarasProvider>
  );
}

// Chat UI component
function ChatUI() {
  const {messages, send, status, isTyping, error, clearSession} = useChat();
  const [input, setInput] = useState('');
  const bottomRef = useRef<HTMLDivElement>(null);

  // Scroll to bottom when messages change
  useEffect(() => {
    bottomRef.current?.scrollIntoView({behavior: 'smooth'});
  }, [messages]);

  function handleSubmit(e: React.FormEvent) {
    e.preventDefault();
    if (!input.trim() || status === 'typing' || status === 'streaming') return;
    send(input.trim());
    setInput('');
  }

  return (
    <div className="chat-container">
      {/* Header */}
      <div className="chat-header">
        <h2>Chat</h2>
        <button onClick={clearSession} disabled={status !== 'idle'}>
          New conversation
        </button>
      </div>

      {/* Messages */}
      <div className="chat-messages">
        {messages.map((msg) => (
          <div key={msg.id} className={`message message--${msg.role}`}>
            {msg.content}
          </div>
        ))}

        {/* Typing indicator — shown while waiting for first token */}
        {isTyping && (
          <div className="message message--assistant message--typing">
            <span className="dot" /><span className="dot" /><span className="dot" />
          </div>
        )}

        {/* Error state */}
        {error && (
          <div className="message message--error">
            {error}
          </div>
        )}

        <div ref={bottomRef} />
      </div>

      {/* Input */}
      <form onSubmit={handleSubmit} className="chat-input-row">
        <input
          value={input}
          onChange={(e) => setInput(e.target.value)}
          placeholder="Type a message..."
          disabled={status === 'typing' || status === 'streaming'}
        />
        <button
          type="submit"
          disabled={!input.trim() || status === 'typing' || status === 'streaming'}
        >
          Send
        </button>
      </form>
    </div>
  );
}
```

## Accessing the SDK directly

If you need to call SDK methods not exposed by `useChat()` (such as `submitLead` or `submitFeedback`), use the `useElaras()` hook to get the underlying `ChatClient` instance and the current session ID:

```tsx
import {useElaras} from '@elaras/react-chat';

function LeadForm() {
  const {client, sessionId} = useElaras();

  async function handleSubmit(email: string, name: string) {
    await client.submitLead(sessionId, {email, name});
  }

  // ...
}
```

`useElaras()` returns `{ client, sessionId, resetSession }` — the same values the provider manages internally.

## TypeScript

Both `@elaras/react-chat` and `@elaras/chat-sdk` are written in TypeScript and ship full type definitions. No `@types/` package needed.

```ts
import type {Message, ChatStatus, UseChatReturn} from '@elaras/react-chat';
import type {ChatbotConfig, ChatMessage, StreamEvent} from '@elaras/chat-sdk';
```
