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

# Migrate the widget from v4 to v5

> Upgrade an existing OpenCX widget to v5. Review package versions, self-hosted scripts, custom rendering, Companion options and session behavior.

Use this guide when upgrading v4 or an earlier v5 beta. Upgrade the package first with your existing configuration, then enable new layouts and capabilities separately.

## Select the release

This guide describes **Widget 5.0.0**. Confirm it is published before using the commands below:

```bash theme={"dark"}
npm view @opencx/widget-react@5.0.0 version
npm view @opencx/widget-react dist-tags --json --prefer-online
```

If 5.0.0 is not available, keep your tested version until publication. Earlier betas do not include all the compatibility changes below. To remain on v4, pin `4.0.63`; `@latest` can move to a new major. Keep the old package versions, script URL and configuration available for rollback.

<Tabs>
  <Tab title="HTML">
    Keep the initialization function and configuration. Change the version in the script URL:

    ```html theme={"dark"}
    <script defer src="https://unpkg.com/@opencx/widget@5.0.0/dist-embed/script.js"></script>
    <script>
      window.addEventListener('DOMContentLoaded', () => {
        window.initOpenScript({ token: '<WIDGET_TOKEN>' });
      });
    </script>
    ```
  </Tab>

  <Tab title="React">
    ```bash theme={"dark"}
    npm install @opencx/widget-react@5.0.0
    ```

    Keep `<Widget options={...} />`. If your app directly imports `@opencx/widget-core` or `@opencx/widget-react-headless`, install them at the same version.
  </Tab>

  <Tab title="Headless">
    ```bash theme={"dark"}
    npm install @opencx/widget-react-headless@5.0.0 @opencx/widget-core@5.0.0
    ```

    Keep the provider, storage adapter and custom UI. Pass a nonempty `components` array containing a `fallback` renderer. See the [headless setup](/widget/custom-components-headless).
  </Tab>
</Tabs>

All directly installed OpenCX widget packages must use the same exact version. The React packages support React 18 and 19. Review your lockfile and any dependency overrides.

## What remains compatible with v4

| Existing setup | Widget 5.0.0 behavior |
| - | - |
| HTML embed | `initOpenScript` and the self-contained `dist-embed/script.js` path remain available. |
| Default layout | The classic popover remains the default; `inline: true` still takes precedence. |
| Polling | Omitted or false `streaming` keeps send/poll delivery, including with the v2 agent and verified users. |
| Authentication | Keep the same server authentication endpoint and `user.token`. The client upgrade does not change chat-only token lifetime. |
| Token renewal | A replacement OpenCX token for the same owner and access scope preserves the active session. Changing user, account or organization resets active state. |
| Customization | Branding, CSS overrides, render slots, header actions, contact forms and starter questions remain configurable. Re-test your own CSS and renderers. |
| Custom user messages | `deliveredAt` remains a compatibility alias; new code can use `timestamp`. |
| Required starter | `requireInitialQuestion` works in v4.0.63 and v5.0.0. Companion shows starters above a disabled composer; the popover hides the composer until selection. |

This is a compatibility contract, not a guarantee for every custom stylesheet, host script or third-party integration. Validate your actual setup before moving an unpinned embed to v5.

## Self-hosted scripts and repeated initialization

Widget 5.0.0's `dist-embed/script.js` is a self-contained classic script. Copying that file is sufficient for the widget code; it does not fetch a separate `widget.js` or additional code chunks. API, attachment and configured asset requests still need your normal host permissions.

Earlier v5 betas used a split loader. Replace that build as one versioned release and keep its old directory available for already-open tabs. Repeated loads of the 5.0.0 script reuse one widget runtime. Initialize one integration on the page; do not mix embed and React mounts in the same root. Test your site's Content Security Policy with the actual script and initializer.

## Review intentional differences

* **HTML sanitization:** agent HTML and footer markup are filtered. Re-test markup and move unsupported presentation into `cssOverrides` or supported custom render slots.
* **Direct schema consumers:** the core package uses `zod` v4 types. Review code that imports its schemas and remove incompatible v3-only overrides.
* **Initialization errors:** the standard widget renders nothing and logs a failure by default. Supply `errorComponent` in a React integration if you need a visible fallback. A missing headless component registry is a setup error that must be fixed separately.
* **Handoff callbacks:** `onHandoff` is not a supported option. `hooks.onMessageReceived` reports a new message, not an immediate handoff event. Do not replace a transfer callback with a message hook and assume equivalent timing.

## Upgrade an earlier v5 beta

Earlier beta integrations may have inherited enabled organization settings. In 5.0.0, opt in explicitly to the features your integration intends to use:

| Feature | Embed opt-in | Additional requirement |
| - | - | - |
| Streamed replies | `streaming: true` | Organization supports streaming. |
| Personal connections | `capabilities.connections: true` | Streaming and a verified token with the intended `mcp_access` scope. |
| Page reading | `features.pageContext: true` | Organization allows page access. |
| Page pointing | Also `features.clientTools: true` | Streaming and a compatible page-effect renderer. |
| Page actions | Also `features.pageActions: true` | Streaming, organization permission and visitor approval for every action. |

Omitted streaming, connections and page flags are off. These client defaults require the upgraded widget; a backend deployment alone does not change an older beta's defaults. To disable page access before upgrading, set all three page flags to `false` explicitly.

Do not enable every flag solely to preserve an old beta's implicit defaults. Enable the features your site uses, with the matching signed account and organization permissions.

## Choose layout and delivery separately

`displayMode: 'companion'` changes presentation. It does not choose the agent version, turn on streaming or grant page permissions. The popover can use supported advanced features too. Inline rendering remains a separate option.

* Omitted or false `streaming` uses polling for saved replies, attachments, human replies and enabled progress updates.
* `streaming: true`, with organization support, streams the live response over Server-Sent Events (SSE). This is not a chat WebSocket connection.
* Polling/history reconciliation still retrieves persisted messages, including human replies and history after reload. It complements the live stream; it does not mean a visitor's message is sent to two agents.
* `presentation.streaming: false` shows complete text while keeping the streaming transport available for live interactions.
* Rich reply generation, selectable follow-up questions and page effects require streaming and supported renderers. Initial starter questions are configuration and work with polling.

See [Companion mode](/widget/companion-mode) for layouts, avatars, bubbles and page permission examples.

## Custom rendering and rich replies

For headless streaming, implement `useAgentChatUi()` alongside the persisted `useMessages()` state. Handle `liveItems`, `turnSources`, completion, Stop, retries and queued messages; a persisted message list alone cannot render the live response.

Declare `richReplies`, `structuredQuestions` or `pageEffects` only after implementing the interaction, including history and consent where applicable. The standard widget declares support for its built-in renderers. Custom replacements must declare their own support.

Existing custom action-result components are separate from the rich-reply catalog. Widget 5.0.0 does not add a customer-defined component catalog or generic rich-reply forms. Personal-connection forms are a separate supported flow.

## Team replies and quoted messages

AI and teammate replies can include `replyTo`. Custom message renderers should preserve the quoted sender and handle a missing quoted message after a partial history load. Companion's `bubbles` and `avatars` are independent; omitted `avatars` follows `bubbles`.

## Keep page context intentional

A `context` object or callback is host-supplied data. It still accompanies messages when automatic page collection is off. Remove or narrow it separately if the data should not be transmitted. `messageCustomData` and `sessionCustomData` are for your team and are separate from agent context.

Use a callback that reads current application state. Strip URL query strings and fragments, and omit private path or title data. Mark sensitive page regions with `data-opencx-private` to exclude them from widget-collected page context. That attribute cannot sanitize data you explicitly supply through another field.

Give interactive controls an accessible name through a visible associated label or `aria-label`. Unnamed dropdowns are excluded from page controls; option text alone does not name the dropdown. Verify that intended controls appear before testing multi-step actions.

Page actions use the site's normal event handlers after visitor approval. Slow loading, navigation or an interrupted event sequence can produce an uncertain result. Test actual site flows and verify the result before retrying a potentially completed operation.

## Verify before rollout

1. Run your unchanged v4 configuration with the new package. Check branding, header callbacks, contact fields, required starters, RTL, mobile layouts and custom renderers.
2. With streaming off, send messages, upload an attachment, receive AI and human replies, and reload history.
3. Renew a real token for the same user; confirm the session remains. Switch A → B → A and verify each account sees only its own permitted history. Test logout and a resumed browser tab.
4. If using streaming, test Stop, a second message during a reply, retries, human handoff and history after reload.
5. Test Companion layouts independently. If enabling page features, test Allow and No, a clarification followed by an action, account switching during a pending action, and slow multi-step flows.
6. For self-hosting, verify the exact served script and existing open tabs. For connections, check authorization, account isolation and renewed tokens.

For rollback, restore the pinned package versions or script URL and prior options together. Changing an npm tag does not replace a cached script or an already installed package.

[Install the migration skill](/widget/agent-skills) if you want your coding assistant to check the same contract. Ask it to use this guide and an exact published version.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.