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

# Installing the snippet

> Add the embed script to your site.

A web widget runs on your site through a single `<script>` tag. The script loads a small loader from Anyreach that injects the chat widget into the page. You only need to add it once, anywhere before the closing `</body>` tag.

## The snippet

Paste this just before `</body>`:

```html theme={null}
<script src="https://your-host/public/embed/{widgetId}" async></script>
```

Replace `{widgetId}` with your widget's ID. You don't have to build this by hand — the **Deploy** tab generates the exact snippet for your widget and host. See [Creating a widget](/web-widgets/creating-a-widget) to get a widget ID.

The `async` attribute lets the script load without blocking page rendering. The loader appends the widget to `document.body` (falling back to `document.documentElement`), so the snippet works regardless of where in the page you place it.

<Note>
  The `src` host is the Anyreach domain serving your widget. The **Deploy** tab fills in the correct host for you, so copy the generated code rather than typing the URL manually.
</Note>

## What the loader does

The script at `/public/embed/{widgetId}` returns JavaScript (served as `application/javascript`) that runs immediately on load. It:

1. Checks the widget's allowed-domain list. If the list is non-empty and the current `window.location.origin` is not on it, the loader logs an error and stops without rendering anything. See [Allowed domains and security](/web-widgets/allowed-domains-and-security).
2. Creates an `<iframe>` with id `anyreach-iframe` pointing at `/public/widgets/{widgetId}`. The real embedding origin is passed as a `parentOrigin` query parameter so the widget knows where it is hosted.
3. Pins the iframe to the bottom-right of the viewport using `position: fixed`, starting at button size (92px x 92px) with a transparent background and the maximum `z-index` so it sits above page content.
4. Exchanges messages with the widget over `postMessage` to resize and reposition.

```
┌─────────────────────────────────────┐
│  Your page                          │
│                                     │
│                                     │
│                          ┌────────┐ │
│                          │ iframe │ │  ← fixed, bottom-right
│                          │  chat  │ │     /public/widgets/{id}
│                          └────────┘ │
└─────────────────────────────────────┘
```

### Desktop vs mobile

The loader listens for `resize` messages from the widget and sizes the iframe to match.

| Mode    | Behavior                                                                                           |
| ------- | -------------------------------------------------------------------------------------------------- |
| Desktop | Floating panel anchored to `right: 0; bottom: 0`, sized to the widget's reported width and height  |
| Mobile  | Full-screen — the iframe is pinned to all four edges (`top`, `left`, `right`, `bottom` set to `0`) |

The widget signals mobile full-screen mode by setting `mobile: true` on its resize message. The loader also sends the host viewport size (`window.innerWidth` / `window.innerHeight`) to the widget on load and on every window `resize`, and responds to `request-viewport` messages, so the widget can decide which layout to use.

### Permissions

The iframe is created with these `allow` permissions so the widget can run voice and other interactive features:

```
microphone; camera; display-capture; autoplay; clipboard-write
```

It also sets `loading = 'lazy'` and `referrerPolicy = 'origin-when-cross-origin'`.

<Warning>
  The loader only honors `postMessage` events whose origin matches the Anyreach host and whose payload carries `source: 'anyreach-widget'`. The `parentOrigin` value is always derived from the real embedding origin, not from forwarded query parameters, so it cannot be spoofed by the embedding page.
</Warning>

## Per-platform instructions

The **Deploy** tab on the widget config screen lists supported platforms. Click a platform to open a dialog with paste-ready instructions and the embed code, then click **Copy**.

| Platform    | Where to add it                                                                    |
| ----------- | ---------------------------------------------------------------------------------- |
| WordPress   | **Appearance → Widgets** (or Code Snippets) → Footer; paste and save               |
| Shopify     | **Online Store → Themes → Edit code → `theme.liquid`** (before `</body>`); paste   |
| Webflow     | **Project Settings → Custom Code → Footer Code**; paste and publish                |
| Wix         | **Settings → Custom Code → Add Code to All Pages** (body end); paste and publish   |
| Squarespace | **Settings → Advanced → Code Injection → Footer**; paste and save                  |
| Next.js     | Add a `next/script` snippet in `app/layout.tsx` with `strategy="afterInteractive"` |
| React       | Add to `public/index.html` before `</body>`                                        |
| Vue         | Add to `public/index.html` before `</body>`                                        |
| Angular     | Add to `src/index.html` before `</body>`                                           |

For most platforms the generated code is the standard `<script>` tag shown above. Next.js is the exception.

### Next.js

Next.js does not allow a raw `<script>` tag in the rendered tree the same way, so use the `next/script` component with the `afterInteractive` strategy:

```tsx theme={null}
// app/layout.tsx
import Script from 'next/script'

<Script src="https://your-host/public/embed/{widgetId}" strategy="afterInteractive" />
```

`afterInteractive` loads the snippet after the page becomes interactive, which is the right time for a non-blocking chat widget.

<Tip>
  React, Vue, and Angular all use the same plain `<script>` tag — just place it in that framework's static HTML entry file. The **Deploy** tab generates the identical snippet for each.
</Tip>

## Next steps

<CardGroup cols={2}>
  <Card title="Creating a widget" icon="plus" href="/web-widgets/creating-a-widget">
    Configure appearance, behavior, and the agent behind the widget.
  </Card>

  <Card title="Allowed domains and security" icon="shield" href="/web-widgets/allowed-domains-and-security">
    Restrict which sites can load the widget.
  </Card>
</CardGroup>
