> ## 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 beta. Review package versions, self-hosted scripts, custom rendering, Companion options and session behavior.

Use this guide when your website already runs the v4 widget. You can keep the classic popover after upgrading, then opt into Companion when you are ready.

<Tip>
  To have your coding assistant review the integration, [install the migration skill](/widget/agent-skills):

  ```bash theme={"dark"}
  npx skills add https://github.com/opencx-labs/widget/tree/64ba5dccdf5fe5997634d807248b790c146f3880 --skill opencx-widget-migration
  ```

  Then ask: “Use opencx-widget-migration to upgrade our v4 widget to v5 beta. Check custom components, authentication and self-hosted assets. Exclude query strings, fragments and private path or title data from page context.”
</Tip>

## Select the v5 version

As of September 7, 2026, v5 is `5.0.0-beta.0` under the `beta` tag, while `latest` remains v4. Check the tags before choosing a target:

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

Keep the previous dependency version, script URL and configuration in version control so you can restore them if needed.

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

    Keep your existing `<Widget options={...} />` integration. If your app also imports types from `@opencx/widget-core`, install that package explicitly at the matching version.
  </Tab>

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

    Keep `WidgetProvider` around your custom UI and review the streaming state described below.
  </Tab>

  <Tab title="HTML">
    Replace the old script URL while keeping initialization after the document is ready:

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

Match every directly installed OpenCX widget package to the selected version. The React packages support React 18 and 19. Let your package manager update the lockfile, and review dependency overrides against the selected packages' declarations.

## Review customizations before shipping

| Area                            | What to check in v5                                                                                                                                                   |
| ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Custom user-message rendering   | Replace `WidgetUserMessage.deliveredAt` with `timestamp`. Account for optional `pending`, `markedElements` and `mentions` fields.                                     |
| HTML replies and footer content | Replies and `chatFooterItems` are sanitized. Re-test custom markup; use `cssOverrides` for styling and supported render slots for richer UI.                          |
| Failed initialization           | `Widget` and `WidgetProvider` render nothing by default after initialization fails and log an error. Provide `errorComponent` when the host needs a visible fallback. |
| Styling and branding            | Re-check composer CSS, opening behavior and reduced motion. Existing `bot` and `textContent` overrides remain the place to set your own identity and header copy.     |
| Visitor identity and storage    | Keep widget and visitor tokens distinct. Check returning visitors, session history and any custom storage adapter after reload.                                       |

For example, a React integration can show a fallback:

```tsx theme={"dark"}
<Widget
  options={{ token: "<WIDGET_TOKEN>" }}
  errorComponent={() => (
    <p>Support chat could not load. Please refresh the page.</p>
  )}
/>
```

## If you self-host the embed

Publish the **entire `dist-embed` directory** under a versioned URL. In v5, `script.js` loads `widget.js`, which can load additional chunks later. Copying only `script.js` is insufficient.

Keep the previous directory available for tabs that are already open. Cross-origin module hosting needs CORS. The loader forwards its script tag's CSP nonce; your policy must also allow your initialization code and later module loads.

## Adopt Companion and streaming

Set `displayMode: 'companion'` to opt into the floating workspace. The default remains `popover`, and `inline: true` takes precedence over either display mode. See [Companion mode](/widget/companion-mode) for a complete setup.

Streaming is enabled for your organization separately. Adding a frontend `streaming: true` option does not enable it. A v5 widget can still use classic replies when streaming is unavailable.

If you render your own headless UI, use `useAgentChatUi()` from `@opencx/widget-react-headless` for the live reply state. `useMessages().messagesState` alone does not cover an in-progress turn. Review `liveItems`, `turnSources`, `isStreaming`, `stop`, `queuedUserMessages`, `removeQueued`, `turnFailed`, `retryFailedTurn` and `pendingClarification` against your installed declarations. The default widget already renders this state.

A message sent while the agent is answering can steer the current task or queue for later. Test both outcomes rather than assuming every second message appears in a queue.

## Review page context and feature controls

Configure website capabilities in [Channels → Web chat](https://platform.open.cx/channels/configure/widget). Agent-wide settings such as scheduled follow-ups live in [Settings → Advanced](https://platform.open.cx/settings/ai-features). Embed options such as `features.pageContext`, `features.dictation` and `features.clientTools` can switch an enabled capability off; they cannot enable a capability disabled by your organization.

Page annotations use **Sees the page**. Remove experimental `enablePageMarks` options; there is no `features.attachments` switch.

Use a `context` function that reads current application state at send time. Build page URLs from `window.location.origin + window.location.pathname` rather than the complete URL, so this page URL excludes query strings and fragments before it is sent with a message and stored in the session. Omit private path or title data too. Apply this constraint to suggestions from the migration skill as well. In beta.0, do not rely on replacing `options.context` to update every sending path; keep changing values in a ref or store that the callback reads. Dispatch `opencx:context-changed` when the current record changes without a URL change.

Host-supplied `context` still accompanies messages when `features.pageContext` is `false`. Remove or narrow your callback separately if that data should no longer be sent.

## Verify before rollout

1. Build and type-check your application with the new packages.
2. Open the widget, send a message, test file attachments and confirm a reply reaches the same session.
3. Reload and check visitor identity, session history, custom components, CSS and human handoff.
4. With streaming enabled, test **Stop**, a second message during a reply, and history after reload.
5. If adopting Companion, test layouts, Escape, mobile sizing, annotations and current context after navigation.
6. For self-hosting, check that every requested module loads, including in a tab opened before the update.

For rollback, restore your previous package versions or script URL and widget options together. Remove v5-only options when returning to v4, and keep the previous self-hosted assets available.
