> ## Documentation Index
> Fetch the complete documentation index at: https://docs.okrapdf.com/llms.txt
> Use this file to discover all available pages before exploring further.

# React Hooks

> React helpers from the @okrapdf/sdk/react subpath.

## Install

```bash theme={null}
npm install @okrapdf/sdk
```

`@okrapdf/sdk/react` is a subpath export of the SDK package, not a separate npm package.

## OkraProvider

Wrap a subtree with `OkraProvider` and provide either a `documentId`, a `url`, or a `file`.

```tsx theme={null}
'use client';

import { OkraProvider } from '@okrapdf/sdk/react';

export function DocumentRoot({ documentId, children }) {
  return (
    <OkraProvider
      apiKey={process.env.NEXT_PUBLIC_OKRA_PUBLISHABLE_KEY}
      documentId={documentId}
    >
      {children}
    </OkraProvider>
  );
}
```

<Warning>
  Use publishable browser keys or a server-side boundary for browser apps. Do not expose `okra_sk_` secret keys in client-side bundles.
</Warning>

## Status and pages

```tsx theme={null}
import { useOkraDocument, useDocumentStatus, usePages } from '@okrapdf/sdk/react';

function DocumentPanel() {
  const { session, documentId, isReady } = useOkraDocument();
  const status = useDocumentStatus(session);
  const pages = usePages(session, { enabled: isReady });

  if (status.error) return <p>{status.error.message}</p>;
  if (!documentId) return <p>Resolving document...</p>;

  return (
    <section>
      <p>{status.data?.phase ?? 'loading'}</p>
      {pages.data.map((page) => (
        <article key={page.pageNumber ?? page.page}>
          <h2>Page {page.pageNumber ?? page.page}</h2>
          <pre>{page.markdown ?? page.content}</pre>
        </article>
      ))}
    </section>
  );
}
```

## Chat

```tsx theme={null}
import { useChat, useOkraDocument } from '@okrapdf/sdk/react';

function ChatPanel() {
  const { session } = useOkraDocument();
  const chat = useChat({ session, stream: true });

  return (
    <form onSubmit={chat.handleSubmit}>
      {chat.messages.map((message) => (
        <p key={message.id}>
          <strong>{message.role}</strong>: {message.content}
        </p>
      ))}
      <input
        value={chat.input}
        onChange={chat.handleInputChange}
        placeholder="Ask about this document"
      />
      <button disabled={chat.isLoading || !session}>Send</button>
    </form>
  );
}
```

## Structured extraction

The React package does not ship a separate `useStructuredOutput` hook. Call `session.prompt(...)` with a schema from your component or wrap it in your app's own state hook.

```tsx theme={null}
import { useState } from 'react';
import { z } from 'zod';
import { useOkraDocument } from '@okrapdf/sdk/react';

const Invoice = z.object({
  vendor: z.string(),
  total: z.number(),
});

function InvoiceFields() {
  const { session } = useOkraDocument();
  const [data, setData] = useState<z.infer<typeof Invoice> | null>(null);

  async function extract() {
    if (!session) return;
    const result = await session.prompt('Extract invoice fields', {
      schema: Invoice,
    });
    setData(result.data ?? null);
  }

  return (
    <section>
      <button onClick={extract} disabled={!session}>Extract</button>
      {data ? <pre>{JSON.stringify(data, null, 2)}</pre> : null}
    </section>
  );
}
```

## DocumentAgent realtime context

For realtime DocumentAgent state and methods, use `createOkraContext(...)`. This requires the `agents` React package in your app.

```tsx theme={null}
import { createOkraContext } from '@okrapdf/sdk/react';

const okra = createOkraContext({
  host: 'https://api.okrapdf.com',
  agent: 'DocumentAgent',
  authEndpoint: '/api/okra/document-token',
});

export function LiveDocument({ documentId }) {
  const document = okra.useDocument(documentId);
  const phase = okra.useDocumentSlice(documentId, (state) => state.phase);

  return <p>{phase ?? document.state?.phase ?? 'connecting'}</p>;
}
```
