---
title: "React SDK"
description: "@ainative/react-sdk — useChat, useCredits, useMemory hooks for React applications"
canonical: "https://docs.ainative.studio/docs/sdks/react"
last-updated: "2026-10-03T21:47:10.764Z"
---

# React SDK

Source: https://docs.ainative.studio/docs/sdks/react

> @ainative/react-sdk — useChat, useCredits, useMemory hooks for React applications

# React SDK

The React SDK provides hooks for chat completions, credit management, ZeroMemory, and more.

```bash
npm install @ainative/react-sdk
```

## Setup

Wrap your app with `AINativeProvider`. The `apiKey` is the only required config option. Optionally pass `baseUrl` to point at a different API endpoint.

```tsx
import { AINativeProvider } from '@ainative/react-sdk';

function App() {
  return (
    <AINativeProvider config={{ apiKey: process.env.REACT_APP_AINATIVE_KEY! }}>
      <YourApp />
    </AINativeProvider>
  );
}
```

:::tip
Never hard-code API keys. Use environment variables (`REACT_APP_AINATIVE_KEY` for CRA, `VITE_AINATIVE_KEY` for Vite) and keep them out of version control.
:::

---

## useChat

Send chat completions and manage conversation state.

```tsx
import { useChat } from '@ainative/react-sdk';

function Chat() {
  const { messages, sendMessage, isLoading, error, reset } = useChat({
    model: 'meta-llama/llama-3.3-70b-instruct',
    temperature: 0.7,
    onError: (err) => console.error('Chat error:', err.message),
  });

  const [input, setInput] = React.useState('');

  const handleSend = async () => {
    if (!input.trim()) return;
    const userMessage = { role: 'user' as const, content: input };
    setInput('');
    await sendMessage([...messages, userMessage]);
  };

  return (
    <div>
      <div style={{ minHeight: 300, overflowY: 'auto' }}>
        {messages.map((msg, i) => (
          <div key={i} style={{ margin: '8px 0' }}>
            <strong>{msg.role === 'user' ? 'You' : 'AI'}:</strong> {msg.content}
          </div>
        ))}
        {isLoading && <div>Thinking...</div>}
        {error && <div style={{ color: 'red' }}>Error: {error.message}</div>}
      </div>

      <div style={{ display: 'flex', gap: 8 }}>
        <input
          value={input}
          onChange={(e) => setInput(e.target.value)}
          onKeyDown={(e) => e.key === 'Enter' && handleSend()}
          placeholder="Type a message..."
          style={{ flex: 1 }}
        />
        <button onClick={handleSend} disabled={isLoading}>Send</button>
        <button onClick={reset}>Clear</button>
      </div>
    </div>
  );
}
```

### Options

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `model` | `string` | — | Model ID (e.g. `meta-llama/llama-3.3-70b-instruct`) |
| `temperature` | `number` | — | Sampling temperature (0-2) |
| `max_tokens` | `number` | — | Maximum response tokens |
| `onError` | `(error: AINativeError) => void` | — | Called on error |
| `onSuccess` | `(response: ChatCompletionResponse) => void` | — | Called on successful completion |

### Return Value

| Field | Type | Description |
|-------|------|-------------|
| `messages` | `Message[]` | Full conversation history |
| `isLoading` | `boolean` | `true` while waiting for a response |
| `error` | `AINativeError \| null` | Last error, if any |
| `response` | `ChatCompletionResponse \| null` | Last raw API response |
| `sendMessage` | `(messages: Message[]) => Promise` | Send messages and get a completion |
| `reset` | `() => void` | Clear conversation state |

The `sendMessage` function accepts the full message array (including history). The hook automatically appends the assistant's reply.

---

## useCredits

Fetch and monitor your API credit balance. The balance is fetched on mount and can be refreshed manually.

```tsx
import { useCredits } from '@ainative/react-sdk';

function CreditDisplay() {
  const { balance, isLoading, error, refetch } = useCredits();

  if (isLoading) return <span>Loading credits...</span>;
  if (error) return <span style={{ color: 'red' }}>Error: {error.message}</span>;

  return (
    <div>
      <h3>Credits: {balance?.remaining_credits.toLocaleString()}</h3>
      <p>Plan: {balance?.plan}</p>
      <p>Used: {balance?.usage_percentage.toFixed(1)}%</p>
      <button onClick={refetch}>Refresh</button>
    </div>
  );
}
```

### Return Value

| Field | Type | Description |
|-------|------|-------------|
| `balance` | `CreditBalance \| null` | Balance data (see below) |
| `isLoading` | `boolean` | `true` while fetching |
| `error` | `AINativeError \| null` | Last error |
| `refetch` | `() => Promise<void>` | Re-fetch balance |

**CreditBalance fields:** `total_credits`, `used_credits`, `remaining_credits`, `plan`, `period_start`, `period_end`, `usage_percentage`.

---

## useMemory

Store and recall memories with [ZeroMemory](/docs/zeromemory/overview). Pass an optional `entityId` to scope all operations to a specific entity (e.g. a user ID). When an `entityId` is provided, existing memories for that entity are loaded automatically on mount.

```tsx
import { useMemory } from '@ainative/react-sdk';

function MemoryPanel({ userId }: { userId: string }) {
  const { memories, remember, recall, forget, isLoading, error } = useMemory(userId);
  const [query, setQuery] = React.useState('');

  const handleRemember = async () => {
    await remember('User prefers dark mode', {
      tags: ['preference', 'ui'],
      importance: 0.8,
    });
  };

  const handleSearch = async () => {
    const results = await recall(query, { limit: 5 });
    console.log('Found', results.length, 'memories');
  };

  return (
    <div>
      <h3>Memories ({memories.length})</h3>

      {isLoading && <p>Loading...</p>}
      {error && <p style={{ color: 'red' }}>{error.message}</p>}

      {memories.map((m) => (
        <div key={m.id} style={{ margin: '4px 0', padding: 8, border: '1px solid #eee' }}>
          <p>{m.content}</p>
          <small>
            Type: {m.memory_type} | Importance: {m.importance} |{' '}
            {m.tags.join(', ')}
          </small>
          <button onClick={() => forget(m.id)} style={{ marginLeft: 8 }}>
            Forget
          </button>
        </div>
      ))}

      <div style={{ marginTop: 16 }}>
        <button onClick={handleRemember}>Remember preference</button>
      </div>

      <div style={{ marginTop: 8, display: 'flex', gap: 8 }}>
        <input
          value={query}
          onChange={(e) => setQuery(e.target.value)}
          placeholder="Search memories..."
        />
        <button onClick={handleSearch}>Recall</button>
      </div>
    </div>
  );
}
```

### API

| Method | Signature | Description |
|--------|-----------|-------------|
| `remember` | `(content: string, options?: RememberOptions) => Promise<Memory \| null>` | Store a new memory |
| `recall` | `(query: string, options?: RecallOptions) => Promise<Memory[]>` | Semantic search over memories |
| `forget` | `(memoryId: string) => Promise<boolean>` | Delete a memory |
| `refetch` | `() => Promise<void>` | Re-load entity memories |

**RememberOptions:** `entity_id`, `memory_type`, `importance` (0-1), `tags` (string[]), `metadata` (Record).

**RecallOptions:** `entity_id`, `layer`, `limit`.

---

## Error Handling

All hooks return an `error` object with the shape `{ message, status?, code? }`. The API also returns structured error responses with `next_action` hints in the body.

```tsx
import { useChat } from '@ainative/react-sdk';

function ChatWithErrorHandling() {
  const { messages, sendMessage, error } = useChat({
    onError: (err) => {
      switch (err.status) {
        case 401:
          // Token expired — redirect to login or refresh
          window.location.href = '/login';
          break;
        case 429:
          // Rate limited — show retry message
          alert('Too many requests. Please wait a moment.');
          break;
        case 503:
          // Service unavailable — the API suggests a fallback model
          console.log('Try a different model');
          break;
        default:
          console.error('Chat error:', err.message);
      }
    },
  });

  // ... render UI
}
```

---

## Full Working Example

A complete chat application with credits display and memory:

```tsx
import React, { useState } from 'react';
import { AINativeProvider, useChat, useCredits, useMemory } from '@ainative/react-sdk';

function ChatApp() {
  const { messages, sendMessage, isLoading, reset } = useChat({
    model: 'meta-llama/llama-3.3-70b-instruct',
  });
  const { balance } = useCredits();
  const { remember } = useMemory('current-user');
  const [input, setInput] = useState('');

  const handleSend = async () => {
    if (!input.trim()) return;
    const text = input;
    setInput('');
    const response = await sendMessage([
      ...messages,
      { role: 'user', content: text },
    ]);

    // Optionally remember the conversation turn
    if (response) {
      await remember(`User asked: ${text}`, { tags: ['conversation'] });
    }
  };

  return (
    <div style={{ maxWidth: 600, margin: '0 auto', padding: 24 }}>
      <header style={{ display: 'flex', justifyContent: 'space-between' }}>
        <h2>AI Chat</h2>
        {balance && <span>{balance.remaining_credits} credits</span>}
      </header>

      <div style={{ minHeight: 400, border: '1px solid #ddd', padding: 16, overflowY: 'auto' }}>
        {messages.map((msg, i) => (
          <div key={i} style={{
            textAlign: msg.role === 'user' ? 'right' : 'left',
            margin: '8px 0',
          }}>
            <span style={{
              display: 'inline-block',
              padding: '8px 12px',
              borderRadius: 12,
              background: msg.role === 'user' ? '#0066ff' : '#f0f0f0',
              color: msg.role === 'user' ? '#fff' : '#000',
            }}>
              {msg.content}
            </span>
          </div>
        ))}
        {isLoading && <div style={{ color: '#888' }}>Thinking...</div>}
      </div>

      <div style={{ display: 'flex', gap: 8, marginTop: 8 }}>
        <input
          value={input}
          onChange={(e) => setInput(e.target.value)}
          onKeyDown={(e) => e.key === 'Enter' && handleSend()}
          placeholder="Type a message..."
          style={{ flex: 1, padding: 8 }}
        />
        <button onClick={handleSend} disabled={isLoading}>Send</button>
        <button onClick={reset}>Clear</button>
      </div>
    </div>
  );
}

// Entry point
export default function App() {
  return (
    <AINativeProvider config={{ apiKey: process.env.REACT_APP_AINATIVE_KEY! }}>
      <ChatApp />
    </AINativeProvider>
  );
}
```

---

## TypeScript Types

Import types directly:

```tsx
import type {
  AINativeConfig,
  Message,
  ChatCompletionResponse,
  CreditBalance,
  AINativeError,
  UseChatOptions,
  UseCreditsReturn,
} from '@ainative/react-sdk';

import type {
  Memory,
  RememberOptions,
  RecallOptions,
  UseMemoryReturn,
} from '@ainative/react-sdk';
```

---

## Next Steps

- [Next.js SDK](/docs/sdks/nextjs) -- Server-side client and auth middleware
- [Build a Chatbot](/docs/examples/chatbot) -- Step-by-step tutorial
- [ZeroMemory](/docs/zeromemory/overview) -- Learn about the memory system
- [API Reference](/docs/api/overview) -- REST API documentation
