> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.withpersona.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.withpersona.com/_mcp/server.

# Embedded Widget

> Embed Relay directly in a web page for the user-facing verification flow.

The Embedded Widget is a [client-side integration method](/relay-getting-started#client-side-integration-methods). It renders the user-facing verification directly in your website and invokes a callback when the flow finishes. No PII is returned to your client.

## Prerequisites

Before rendering the Widget, your server must create a Relay session and return the Relay session access token to your web client. See [server-side integration methods](/relay-getting-started#server-side-integration-methods).

> **Warning**
>
> Store the Relay secret securely on your server. Never expose it to the client.

## Embed the Widget

![The Embedded Widget uses the Relay session access token to run the user-facing verification.](https://assets.withpersona.com/f_auto,q_auto/developer-docs/images/relay-integration-overview-phase2.png)

**Install**

Available on npm: [`@persona/relay`](https://www.npmjs.com/package/@persona/relay)

#### npm

```bash
npm install @persona/relay
```

#### yarn

```bash
yarn add @persona/relay
```

**Add a container element**

Add a `div` to your HTML where the Widget will render.

```html
<div id="relay-container"></div>
```

The first argument accepts either a CSS selector string or a direct DOM element reference.

```javascript
// A CSS selector that targets the element with id="relay-container"
new Relay("#relay-container", options);

// A DOM element, such as a React ref
new Relay(containerRef.current, options);
```

**Initialize**

```javascript
import Relay from "@persona/relay";

const relay = new Relay("#relay-container", {
  accessToken: "<relay-session-access-token from your server>",
  theme: "auto", // 'light' | 'dark' | 'auto' (default: 'auto')
  onComplete: () => {
    // Ask your server to retrieve the claim result.
  },
  onExpire: () => {
    // The Relay session expires after 30 minutes.
    // Create a new Relay session on your server, then initialize again.
  },
  onError: (error) => {
    console.error(error);
  },
});
```

During this step:

* The Widget renders the configured verification experience.
* The user completes the flow inside the Widget.
* `onComplete` signals that the user-facing flow finished. It does not return PII or the claim result.

When `onComplete` runs, ask your server to retrieve the claim result with the Relay token, Relay secret, and Privacy Pass token stored during the server-side phases. See [server-side integration methods](/relay-getting-started#server-side-integration-methods).

## Cleanup

Call `relay.destroy()` to unmount the Widget and clean up resources when it is no longer needed.

## Try the Embedded Widget