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

# React

> Idiomatic React components wrapping the Qirtaas mount API.

`@qirtaas/react` wraps [`@qirtaas/core`](/sdk/editor)'s mount API in two
components: `<QirtaasEditor>` and `<QirtaasRenderer>`. The editor itself is a
self-contained island that core bundles in — **your React app does not need Vue
installed**, and only React is a peer dependency.

```bash theme={null}
npm install @qirtaas/react
```

## QirtaasEditor

```tsx theme={null}
import { useRef } from "react";
import { QirtaasEditor, type QirtaasEditorHandle } from "@qirtaas/react";

function Compose({ docId }: { docId?: string }) {
  const editor = useRef<QirtaasEditorHandle>(null);

  return (
    <div style={{ height: "70vh" }}>
      <QirtaasEditor
        ref={editor}
        documentId={docId}
        getToken={fetchEmbedToken}
        locale="ar"
        theme="light"
        onDocumentCreated={(id) => saveIdSomewhere(id)}
        onSaveStateChange={(state) => setBadge(state)}
      />
    </div>
  );
}
```

<Warning>
  The editor owns an internal scroll view (pinned toolbar, scrolling body) and
  fills its container — the parent element must give it a **bounded height**
  (e.g. a sized dialog body or `h-[60vh]`).
</Warning>

### Props

Props mirror [`EditorMountOptions`](/sdk/editor#editormountoptions), plus the
connection config (the wrapper creates its client internally):

<ParamField path="getToken" type="() => Promise<string> | string" required>
  Returns a short-lived embed token. Called on init, before expiry, and on
  `401`. See [Authentication](/backend/authentication).
</ParamField>

<ParamField path="apiUrl" type="string" default="https://api.qirtaas.io">
  Qirtaas API base URL.
</ParamField>

<ParamField path="documentId" type="string">
  Existing document to load. Omit to lazy-create on first edit
  (`onDocumentCreated` delivers the new id).
</ParamField>

<ParamField path="initialContent" type="Json | null">
  Initial content (TipTap JSON) when not loading from a `documentId`.
</ParamField>

<ParamField path="locale" type="&#x22;en&#x22; | &#x22;ar&#x22;" />

<ParamField path="theme" type="&#x22;light&#x22; | &#x22;dark&#x22;" />

<ParamField path="readOnly" type="boolean" default="false" />

<ParamField path="autofocus" type="boolean" />

<ParamField path="autosave" type="AutosaveOptions">
  See [Types](/sdk/types#autosaveoptions). Set `{ enabled: false }` and call
  `save()` on the ref to drive persistence yourself.
</ParamField>

Callback props (`onReady`, `onChange`, `onSaveStateChange`,
`onDocumentCreated`, `onError`, `onTokenExpired`, `onEvent`) match the
[editor callbacks](/sdk/editor#callbacks) one-to-one. Always the latest render's
callbacks are invoked — capturing state in them is safe.

### Live updates vs. remount

The component mounts the editor **once**. Only two props are live after mount:

* `theme` — forwarded to `setTheme`
* `readOnly` — forwarded to `setEditable`

Changing anything else (`documentId`, `getToken`, `locale`, …) requires a
remount — give the component a `key` that changes with them:

```tsx theme={null}
<QirtaasEditor key={docId} documentId={docId} getToken={getToken} />
```

<Note>
  The mount effect is StrictMode-safe: the dev-mode double invoke fully tears
  down and cleanly re-mounts.
</Note>

### Ref handle (`QirtaasEditorHandle`)

<ResponseField name="getJSON()" type="Json | null">
  Current editor content (TipTap JSON).
</ResponseField>

<ResponseField name="save()" type="Promise<void>">
  Force an immediate save of pending changes.
</ResponseField>

<ResponseField name="setEditable(editable)" type="void" />

<ResponseField name="setTheme(theme)" type="void" />

## QirtaasRenderer

Read-only display. Supply exactly **one** auth source — `shareToken`,
`getToken`, or `getSignature` (see [Renderer auth modes](/sdk/renderer#auth-modes)).

```tsx theme={null}
import { QirtaasRenderer } from "@qirtaas/react";

<QirtaasRenderer
  documentId={docId}
  getSignature={() => fetchSignature(docId)} // cross-user read
  locale="ar"
  theme="light"
  onError={(code) => report(code)}
/>
```

### Props

<ParamField path="apiUrl" type="string" default="https://api.qirtaas.io" />

<ParamField path="documentId" type="string">
  Document to render. With `shareToken`, the token resolves the document.
</ParamField>

<ParamField path="shareToken" type="string">Public read.</ParamField>

<ParamField path="getToken" type="() => Promise<string> | string">
  Own-document read (embed token).
</ParamField>

<ParamField path="getSignature" type="() => Promise<{ signature: string; exp: number }>">
  Cross-user read (per-document HMAC signature).
</ParamField>

<ParamField path="locale" type="&#x22;en&#x22; | &#x22;ar&#x22;" />

<ParamField path="theme" type="&#x22;light&#x22; | &#x22;dark&#x22;" />

Callbacks: `onReady`, `onError`. Ref handle (`QirtaasRendererHandle`) exposes
`setTheme(theme)`. As with the editor, only `theme` is live — remount (via
`key`) for anything else.

## Imperative client operations

For mount-less operations (e.g. driving a document list view), the package
re-exports the [client](/sdk/client):

```tsx theme={null}
import { createQirtaasClient } from "@qirtaas/react";

const qirtaas = createQirtaasClient({ getToken: fetchEmbedToken });
const docs = await qirtaas.listDocuments(); // [{ id, title, … }]
await qirtaas.deleteDocument(id);
```
