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

# Embed the document viewer widget

> Add the Bigdata document viewer widget to your website with a script tag, a backend proxy for your API key, and optional theming. Load public or private documents in the viewer.

The Bigdata document viewer widget loads as a **single script** - no separate CSS file to host. You mount it on a container element and point it at a **URL you control** - your backend adds your Bigdata API credentials and forwards requests to the document and content APIs. The UI renders the document (including PDF) in an **open Shadow DOM** so the host page’s own CSS does not restyle the viewer’s internals.

## How it fits together

Traffic for document data goes through **your** backend: the widget only calls `proxyUrl` - your server holds the Bigdata credentials, reaches Bigdata’s **Documents** and **Content** APIs, and returns JSON (and file URLs) the viewer can use.

**Where each part runs** - the document viewer and your proxy live on **your** side; file storage, annotation, and catalog APIs run in **Bigdata**'s infrastructure.

```mermaid theme={null}
flowchart LR
  subgraph yours [Your infrastructure]
    direction TB
    FE[Document viewer]
    BE["Your backend / proxy"]
  end
  subgraph bigdata [Bigdata infrastructure]
    API[Documents & Content API]
  end
  FE --> BE
  BE --> API
  API --> BE
  BE --> FE
```

**What happens at each step** - the browser issues **GET** requests to your `proxyUrl`; your server adds the API key, calls Bigdata, and returns **RPJSON** and signed URLs. Time-limited URLs are often re-fetched through **`/proxy-document`** on your side so the browser does not need direct cross-origin access to signed storage or your key.

```mermaid theme={null}
sequenceDiagram
  participant W as Document viewer widget
  participant P as Your backend
  participant B as Bigdata APIs
  W->>P: GET (document path or proxy-document)
  Note over P: Attach API key server-side
  P->>B: Forward
  B-->>P: JSON, signed URL metadata
  P-->>W: Response
```

<Warning>
  Never put your Bigdata API key in frontend code or expose it in the browser.
  The widget is designed to call **your** proxy. The proxy must attach your API
  key when calling Bigdata.
</Warning>

## Why you need a backend endpoint

End users can open developer tools and read any secret shipped to the page. The widget therefore sends **only** to **`proxyUrl`**. Your server (or serverless function, or BFF) should:

1. Authenticate the end user if needed (cookie, session, or signed token).
2. Add the Bigdata API key to the outbound request when you call Bigdata.
3. **Forward** the external paths the widget uses **`GET /proxy-document?url=…`** when dealing with a **signed** URL (without adding authentication).

## Browser compatibility

The script bundle targets **modern evergreen browsers**. It is **not** supported in Internet Explorer or other legacy engines without current Web APIs.

**Supported browsers**

| Browser     | Guidance                                                                                                                                                                                                   |
| ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Chrome**  | Recent stable (desktop and Android).                                                                                                                                                                       |
| **Edge**    | Recent stable (Chromium-based).                                                                                                                                                                            |
| **Firefox** | Recent stable.                                                                                                                                                                                             |
| **Safari**  | **16.4 or newer** on macOS and iOS. Earlier versions lack reliable support for **constructible stylesheets** and **`adoptedStyleSheets`** on the widget’s Shadow DOM, which the bundle uses to inject CSS. |

**Environment expectations**

* **JavaScript enabled** - the widget mounts via script and does not render without JS.
* **CORS to your `proxyUrl`** - if the embed and the API are on different origins, allow `GET` (and preflight when **`getHeaders`** adds non-simple request headers). Misconfigured CORS is a common “nothing loads” symptom, see [Troubleshooting](#troubleshooting).
* **Third-party cookies** - not required for the default embed pattern - auth is between the visitor’s browser and **your** `proxyUrl` (or headers from **`getHeaders`**, or cookies you set on that origin).

For audiences on older locked-down browsers, contact [support](/support) to discuss feasibility.

## Quickstart

<Steps>
  <Step title="Add a sized container">
    Add a wrapper element and **give it the size you want** (fixed pixels, percentage, flex, grid, or `min-height`-whatever fits your page). The widget mounts inside it and **stretches to fill** the container’s box.

    ```html theme={null}
    <div id="document-viewer" style="width: 100%; min-height: 480px;"></div>
    ```
  </Step>

  <Step title="Load the script">
    Use the widget script URL from Bigdata. Example shape (version path may differ). Load it **before** the inline script that constructs the widget so `BigdataDocumentViewer` is defined:

    ```html theme={null}
    <script src="https://app.bigdata.com/widgets/document-viewer/v1.0.0/document-viewer.min.js"></script>
    ```
  </Step>

  <Step title="Initialize the widget">
    Create the widget with **`new BigdataDocumentViewer.BigdataDocumentWidget({ ... })`**. At minimum set **`container`** and **`proxyUrl`**. Set **`openDocument`** when you know which file to show first, and add **`theme`** and **`getHeaders`** as needed-see the [configuration reference](#configuration-reference) below.

    ```html theme={null}
    <script>
      new BigdataDocumentViewer.BigdataDocumentWidget({
        container: "#document-viewer",
        instanceId: "main-doc",
        proxyUrl: "https://your-backend.com/bff/bigdata",
        openDocument: {
          documentId: "YOUR_PUBLIC_DOCUMENT_ID",
          isPrivate: false,
        },
        theme: {
          preset: "light",
        },
      })
    </script>
    ```
  </Step>
</Steps>

<Note>
  The **Bigdata** web app’s **Widget Playground** (under **Playground** →
  **Widgets**) loads the same UMD and wires **`getHeaders`** to the signed-in
  user-use it as a live example when you integrate auth.
</Note>

## Global API: `BigdataDocumentViewer`

After the bundle loads, the UMD global **`BigdataDocumentViewer`** exposes **`BigdataDocumentWidget`**, and (for multiple widgets) **`BigdataWidgetManager`**. The legacy helper **`init(config)`** is an alias for `new BigdataDocumentWidget(config)` and returns the mounted instance.

**UMD `BigdataDocumentViewer` members**

<table className="widget-mdx-data-table">
  <thead>
    <tr>
      <th>Property / method</th>
      <th>Type</th>
      <th>Description</th>
    </tr>
  </thead>

  <tbody>
    <tr>
      <td>
        <code>BigdataDocumentViewer.init</code>
      </td>

      <td>
        <code>function</code>
      </td>

      <td>
        <strong>Deprecated.</strong> <code>init(config)</code> is a legacy alias for <code>new BigdataDocumentWidget(config)</code>.
      </td>
    </tr>

    <tr>
      <td>
        <code>BigdataDocumentViewer.BigdataDocumentWidget</code>
      </td>

      <td>
        <code>class</code>
      </td>

      <td>Constructor: pass the same <code>config</code> as above.</td>
    </tr>

    <tr>
      <td>
        <code>BigdataDocumentViewer.BigdataWidgetManager</code>
      </td>

      <td>
        <code>class</code>
      </td>

      <td>
        Optional. Shared <code>manager</code> and base <code>theme</code> across
        widgets.
      </td>
    </tr>
  </tbody>
</table>

Store the instance from `new BigdataDocumentViewer.BigdataDocumentWidget(...)` if you need to call `destroy()` or use **`addListener`** / **`emit`**. (The legacy `BigdataDocumentViewer.init(...)` helper is deprecated.)

## Proxy contract (summary)

**What your BFF should serve (per path)**

<table className="widget-mdx-data-table proxy-contract-data-table">
  <thead>
    <tr>
      <th>Aspect</th>
      <th>Expected behavior</th>
    </tr>
  </thead>

  <tbody>
    <tr>
      <td>Public doc</td>

      <td>
        <strong>GET</strong> <code>\{proxyUrl}/v1/documents/\{documentId}</code>. The response is either a <strong>limited</strong> view or includes a <strong>signed</strong> <code>url</code> for the full annotated content - the widget may follow with <code>/proxy-document</code> to load <strong>RPJSON</strong>. Align with <a href="/api-reference/search/fetch-document">Fetch document</a>.
      </td>
    </tr>

    <tr>
      <td>Private doc</td>

      <td>
        <strong>GET</strong> <code>\{proxyUrl}/contents/v1/documents/\{documentId}/annotated</code>. The JSON includes a <strong>signed</strong> <code>url</code>; the viewer loads RPJSON via <code>/proxy-document</code>. See <a href="/api-reference/documents/get-annotated-document">Get annotated document</a>.
      </td>
    </tr>

    <tr>
      <td>Proxy pass-through</td>

      <td>
        <strong>GET</strong> <code>\{proxyUrl}/proxy-document?url=\{encodeURIComponent(signedUrl)}</code> - your server fetches <code>signedUrl</code>, returns the body to the client (RPJSON or bytes). <strong>Required</strong> when the browser must not call the signed host directly.
      </td>
    </tr>

    <tr>
      <td>Original file</td>

      <td>
        <strong>GET</strong> <code>\{proxyUrl}/contents/v1/documents/\{documentId}/original</code> for PDF / download flows, as used by the file URL helper. Match <a href="/api-reference/documents/get-original-document">Get original document</a> style responses.
      </td>
    </tr>
  </tbody>
</table>

**Headers** - if you set **`getHeaders`**, the widget merges the returned map onto requests the HTTP client makes to your **`proxyUrl`** (for example `Authorization: Bearer ...`).

**Buffering and JSON** - if the viewer stays on “loading” while your proxy already finished, check that your framework is not transforming or stripping the JSON body, and that **CORS** and **`proxy-document`** are implemented for **GET**.

## Configuration reference

### Top-level options

**`config` options (top level)**

<table className="widget-mdx-data-table">
  <thead>
    <tr>
      <th>Option</th>
      <th>Type</th>
      <th>Description</th>
    </tr>
  </thead>

  <tbody>
    <tr>
      <td>
        <code>container</code>
      </td>

      <td>
        <code>string</code> or <code>HTMLElement</code>
      </td>

      <td>
        <strong>Required.</strong> Where the widget mounts: a CSS selector that <code>document.querySelector</code> resolves, or a DOM element.
      </td>
    </tr>

    <tr>
      <td>
        <code>proxyUrl</code>
      </td>

      <td>
        <code>string</code>
      </td>

      <td>
        <strong>Required (for loading documents).</strong> The base of your BFF.
        Without it, the inner queries for RPJSON will not run.
      </td>
    </tr>

    <tr>
      <td>
        <code>instanceId</code>
      </td>

      <td>
        <code>string</code>
      </td>

      <td>
        Optional. Stable id for this instance when several widgets share a page;
        if omitted, the implementation uses <code>"document"</code>.
      </td>
    </tr>

    <tr>
      <td>
        <code>manager</code>
      </td>

      <td>
        <code>BigdataWidgetManager</code>
      </td>

      <td>
        Optional. Shared manager across widgets for coordinated events and a
        common base theme.
      </td>
    </tr>

    <tr>
      <td>
        <code>getHeaders</code>
      </td>

      <td>
        <code>function</code>
      </td>

      <td>
        Optional. <code>() => Promise\<Record\<string, string>></code> -
        merged onto requests to your origin (e.g. bearer for your BFF).
      </td>
    </tr>

    <tr>
      <td>
        <code>openDocument</code>
      </td>

      <td>
        <code>object</code>
      </td>

      <td>
        Optional. Initial <code>documentId</code>, <code>isPrivate</code>, and
        optional <code>matchesInformation</code>; see <strong>
        <code>openDocument</code> (shape)
        </strong>.
      </td>
    </tr>

    <tr>
      <td>
        <code>theme</code>
      </td>

      <td>
        <code>object</code>
      </td>

      <td>
        Optional. Visual settings; see <strong>
        <code>theme</code>
        </strong>.
      </td>
    </tr>
  </tbody>
</table>

### `openDocument` (shape)

**`openDocument` fields**

<table className="widget-mdx-data-table">
  <thead>
    <tr>
      <th>Field</th>
      <th>Type</th>
      <th>Description</th>
    </tr>
  </thead>

  <tbody>
    <tr>
      <td>
        <code>documentId</code>
      </td>

      <td>
        <code>string</code>
      </td>

      <td>
        <strong>Required</strong> when you open a document. The public document
        id or private <strong>content</strong> id your backend uses.
      </td>
    </tr>

    <tr>
      <td>
        <code>isPrivate</code>
      </td>

      <td>
        <code>boolean</code>
      </td>

      <td>
        <code>true</code> = private <strong>Content</strong> flow (
        <code>/contents/v1/.../annotated</code>); <code>false</code> = public <code>GET /v1/documents/\{id}</code> path.
      </td>
    </tr>

    <tr>
      <td>
        <code>matchesInformation</code>
      </td>

      <td>
        <code>object</code>
      </td>

      <td>
        <strong>Optional</strong> highlighting: <code>\{ cqsChunks: \[...] }</code> or <code>\{ cnums: number\[] }</code> from your search or app, when you
        need chunk alignment.
      </td>
    </tr>
  </tbody>
</table>

**Later navigation** - call **`instance.emit("opendocument", { documentId, isPrivate, matchesInformation? })`** to load another document (same instance).

### `theme`

The widget runs inside an **open Shadow DOM**, so host-page CSS does not change its internal layout or colors. **Size the mount container** in your page - the widget **fills that container**. You can also set **`theme.width`** and **`theme.height`** if you need explicit dimensions on the widget instead of relying on the container alone.

Theme values are merged with defaults. **`preset`** chooses a base palette before overrides:

**`theme.preset` values**

<table className="widget-mdx-data-table">
  <thead>
    <tr>
      <th>
        <code>preset</code>
      </th>

      <th>Behavior</th>
    </tr>
  </thead>

  <tbody>
    <tr>
      <td>
        <code>light</code>
      </td>

      <td>Light background and text colors.</td>
    </tr>

    <tr>
      <td>
        <code>dark</code>
      </td>

      <td>Dark background and text colors.</td>
    </tr>

    <tr>
      <td>
        <code>auto</code>
      </td>

      <td>
        Follows the user’s system preference (
        <code>prefers-color-scheme</code>).
      </td>
    </tr>
  </tbody>
</table>

**`theme` field reference**

<table className="widget-mdx-data-table">
  <thead>
    <tr>
      <th>Field</th>
      <th>Type</th>
      <th>Description</th>
    </tr>
  </thead>

  <tbody>
    <tr>
      <td>
        <code>primaryColor</code>
      </td>

      <td>
        <code>string</code>
      </td>

      <td>Accents (controls, highlights).</td>
    </tr>

    <tr>
      <td>
        <code>backgroundColor</code>
      </td>

      <td>
        <code>string</code>
      </td>

      <td>Widget background.</td>
    </tr>

    <tr>
      <td>
        <code>surfaceColor</code>
      </td>

      <td>
        <code>string</code>
      </td>

      <td>Surfaces and panels.</td>
    </tr>

    <tr>
      <td>
        <code>textColor</code>
      </td>

      <td>
        <code>string</code>
      </td>

      <td>Primary text.</td>
    </tr>

    <tr>
      <td>
        <code>textSecondaryColor</code>
      </td>

      <td>
        <code>string</code>
      </td>

      <td>Muted text.</td>
    </tr>

    <tr>
      <td>
        <code>borderColor</code>
      </td>

      <td>
        <code>string</code>
      </td>

      <td>Borders and dividers.</td>
    </tr>

    <tr>
      <td>
        <code>fontFamily</code>
      </td>

      <td>
        <code>string</code>
      </td>

      <td>
        Font stack; can default like the <strong>chat</strong> widget to
        inherit the host.
      </td>
    </tr>

    <tr>
      <td>
        <code>fontSize</code>
      </td>

      <td>
        <code>string</code>
      </td>

      <td>
        Base size; defaults in the same family as the chat widget (e.g. <code>16px</code>).
      </td>
    </tr>

    <tr>
      <td>
        <code>borderRadius</code>
      </td>

      <td>
        <code>string</code>
      </td>

      <td>Corner radius (e.g. <code>8px</code>).</td>
    </tr>

    <tr>
      <td>
        <code>width</code>
      </td>

      <td>
        <code>string</code>
      </td>

      <td>Optional fixed width when not using a fully sized container.</td>
    </tr>

    <tr>
      <td>
        <code>height</code>
      </td>

      <td>
        <code>string</code>
      </td>

      <td>Optional fixed height when not using a fully sized container.</td>
    </tr>

    <tr>
      <td>
        <code>preset</code>
      </td>

      <td>
        <code>"light" | "dark" | "auto"</code>
      </td>

      <td>Base palette; see the table above.</td>
    </tr>
  </tbody>
</table>

## Multiple widgets and shared theming

Create one **`BigdataWidgetManager`** with an optional shared `theme`, pass it as `manager` in each **`BigdataDocumentWidget` config**, and give each widget a distinct **`instanceId`**. That way instances stay isolated but can share base styling-**including** when you add the **chat** widget and **document viewer widget** on the same page.

## Instance lifecycle and events

* **`destroy()`** - Unmounts React, clears the shadow root, and unregisters the instance. Call this when removing the widget from the page.
* **`addListener(event, handler)`** / **`removeListener(event, handler)`** - Subscribe to widget events.

Event payloads include `instanceName`, `timestamp`, and `data`:

**`data` payload (summary) per event**

<table className="widget-mdx-data-table">
  <thead>
    <tr>
      <th>Event</th>

      <th>
        <code>data</code> shape (summary)
      </th>
    </tr>
  </thead>

  <tbody>
    <tr>
      <td>
        <code>ready</code>
      </td>

      <td>
        <code>\{ widgetName: "BIGDATA\_DOCUMENT\_WIDGET" }</code> (fires after mount).
      </td>
    </tr>

    <tr>
      <td>
        <code>opendocument</code>
      </td>

      <td>
        Same as <strong>openDocument</strong>: <code>documentId</code>, <code>isPrivate</code>, optional <code>matchesInformation</code>.
      </td>
    </tr>
  </tbody>
</table>

(For parity with the **chat** bus, the same type names exist across widgets. The document viewer widget is typically driven with **`opendocument`** to change what is on screen, see **`openDocument` (shape)** in [Top-level options](#top-level-options).)

## Troubleshooting

<AccordionGroup>
  <Accordion title="Blank area or widget does not appear">
    Confirm the script URL loads (network tab, no 404). Ensure `container`
    matches an existing element. Check the browser console for validation errors
    (for example invalid `proxyUrl`). The inner fetch also needs a valid
    **`openDocument` / `emit("opendocument", ...)`** with a **`documentId`**,
    and a configured **`proxyUrl`**, or nothing will request RPJSON.
  </Accordion>

  <Accordion title="CORS errors">
    Your `proxyUrl` origin must allow the site origin that embeds the widget
    (`Access-Control-Allow-Origin` and related headers for `GET` and, if you use
    `getHeaders`, the necessary preflight).
  </Accordion>

  <Accordion title="401 or 403 from proxy">
    The proxy is rejecting the request before Bigdata. Verify server-side auth
    and that forwarded credentials and **`getHeaders`** match what your BFF
    expects.
  </Accordion>

  <Accordion title="Document id works in the app but not the embed">
    For **Content**-originated files, set **`isPrivate: true`** and the
    **`content_id`** your proxy expects. For **public** catalog content, set
    **`isPrivate: false`** and the id you would pass to [Fetch
    document](/api-reference/search/fetch-document). Mixing public vs private
    flags is a frequent cause of 404s at the proxy.
  </Accordion>

  <Accordion title="RPJSON never appears; loading never finishes">
    A missing or incorrect **`/proxy-document`** is common: the first response
    returns a **signed** `url` that the browser must not call cross-origin.
    Ensure **`GET .../proxy-document?url=`** downloads the **server-side** and
    returns the JSON. If your BFF is slow but correct, also confirm you are not
    stripping or buffering the response body. Check upstream **API key**
    pass-through for **`/v1/documents/...`** and **`/contents/.../annotated`**.
  </Accordion>
</AccordionGroup>

For personalized help, use [Support](/support).
