Install
@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.
'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>
);
}
Use publishable browser keys or a server-side boundary for browser apps. Do not expose okra_sk_ secret keys in client-side bundles.
Status and pages
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
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>
);
}
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.
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.
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>;
}