# Embed your help center in your app

_Category: Widget and embedding_

Show your whole help center, with its search, categories and articles, as a page of your website or app. A short script creates a frame that fits the height of each page and keeps your page's address in step with what the reader is viewing.

The embed suits a dedicated help page. To offer help next to your own interface, on any page, use the widget instead; see [Widget installation and options](https://developers.helpcenter.io/content/widget-options).

**Who can do this:** Owners and Admins allow your website, and whoever edits your website's code adds the embed

## Before you start

- **Allow the page's host.** Add the host of the page that shows the help center, such as `app.example.com`, to **Embedding origins** in **Settings**. Until it's there, browsers refuse to show your help center in the frame. See [Choose which websites can show the widget](https://self.helpcenter.io/content/embedding-origins), and for how entries match, [Content Security Policy and allowed origins](https://developers.helpcenter.io/content/content-security-policy).
- **Serve the page over HTTPS on the standard port.** Browsers show the frame only on `https://` pages without a port number. A page at `http://localhost:3000` or `http://127.0.0.1:8080` can't show it, even with its host in the list.
- **List every page in the chain.** If your page is itself shown inside another page, such as a reporting tool or a website builder's custom-code frame, every host in that chain must be in Embedding origins.

## Add the embed

Put this where the help center should appear, with your help center's subdomain in place of `acme` (for `acme.helpcenter.io`):

```
<div id="hc_embed"></div>
<script>
  var subdomain = 'acme';
</script>
<script src="https://helpcenter.io/js/embed.js"></script>
```

- `<div id="hc_embed">` is where the frame goes. It must come before the embed script, which looks for it when it runs. Style it like any element of your page; the frame fills its width.
- `subdomain` must be a global variable, declared with `var` in a regular script before the embed script.
- `embed.js` loads from `helpcenter.io` for every help center.

### Help centers on a custom domain

If your help center uses a custom domain, name it in `window._hcEmbedOpts`:

```
<div id="hc_embed"></div>
<script>
  var subdomain = 'acme';
  window._hcEmbedOpts = { customDomain: 'help.acme.com' };
</script>
<script src="https://helpcenter.io/js/embed.js"></script>
```

Without `customDomain`, the frame still shows your help center, but it can't adjust its height or follow links: a help center with a custom domain moves visitors from its `helpcenter.io` address to the custom domain, and the embed script only listens to the address it loaded.

| Setting | Where | What it does |
| --- | --- | --- |
| `subdomain` | Global variable | Required unless you set `customDomain`. Your help center's subdomain, such as `'acme'`. |
| `customDomain` | `window._hcEmbedOpts` | Required when your help center has a custom domain. The domain only, without `https://`, such as `'help.acme.com'`. The frame loads your help center from it. |

## How the embed behaves

- **Height.** The frame takes the height of each help center page when it loads, so visitors scroll your page as usual. Content that grows after the page has loaded isn't measured again.
- **Navigation.** Links between pages of your help center open inside the frame, and your page's address follows along, for example `https://app.example.com/help#/en/content/reset-your-password`. Reloading or sharing that address opens the same help center page.
- **Search.** Searches run inside the frame. Your page's address then shows `#/search`, without the search terms.
- **Links that don't open.** Links to other websites, email links, and links to a heading on the same page do nothing inside the embed.
- **Your page's fragment.** The embed writes to your page's URL fragment (the part after `#`), so it doesn't fit a page that uses the fragment for its own routing.
- **Use the script, not a plain `<iframe>`.** In a plain frame, links inside your help center don't open.

The frame has no title, which screen readers announce. Give it one after the embed script:

```
<div id="hc_embed"></div>
<script>
  var subdomain = 'acme';
</script>
<script src="https://helpcenter.io/js/embed.js"></script>
<script>
  document.querySelector('#hc_embed iframe').setAttribute('title', 'Help center');
</script>
```

## Private help centers

On a private help center, readers must be signed in inside the frame, with JWT single sign-on. How long that sign-in lasts depends on where your help center lives:

- **On your own site, it works.** With a custom domain on your own domain, such as `help.example.com` inside `app.example.com`, the frame's first page sends the reader through your Login URL, and a session keeps them signed in on every page after that. Your Login URL runs inside the frame: for a reader who is already signed in to your app, it sends them straight back.
- **On another site, sign-in doesn't hold.** With `acme.helpcenter.io` inside `app.example.com`, browsers don't keep the help center's session cookie in the frame. The embed script's frame never gets past sign-in: each sign-in sends it back to your Login URL, in a loop when your Login URL signs readers straight back in. A token in `?jwt=` on a frame's first address signs in that page only, and the next page goes back to your Login URL. See [Troubleshoot single sign-on](https://developers.helpcenter.io/content/troubleshoot-sso).

For private content on a page of another site, use the widget with JWT sign-in, which sends a token with every request and needs no cookie; see [Sign readers into the widget with a JWT](https://developers.helpcenter.io/content/widget-jwt). The embed script has no option for a token. How `?jwt=` and the Login URL sign readers in is in [How JWT single sign-on works](https://developers.helpcenter.io/content/how-jwt-sso-works).

## Show the help center only inside your app

With **JS-only access** on, in **Settings** → **General**, your help center is shown through the embed and the widget on the websites in Embedding origins, rather than as a website of its own (see [Show your help center only inside your app](https://self.helpcenter.io/content/js-only-access)):

- Opening one of its pages directly shows "Help center not available." with the status `403 Forbidden`.
- Following a link to it from a website that isn't in Embedding origins shows "Embedding not allowed."
- The embed and the widget keep working on the websites in Embedding origins.

JS-only access keeps the help center from being browsed as a normal website, but it is not access control. To limit who can read your articles, make the help center Private and sign readers in with a JWT.

It relies on the address browsers send along with each request (the referrer). A page with `Referrer-Policy: no-referrer` can't show the embed: the frame shows "Help center not available." Keep at least the origin in the referrer, as the default policy, `strict-origin-when-cross-origin`, does.

## Troubleshooting

**The frame stays blank.** Open the browser console. A message like this names the page the browser objected to, and lists the hosts your help center allows right now:

```
Framing 'https://acme.helpcenter.io/' violates the following Content Security Policy
directive: "frame-ancestors 'self' app.example.com". The request has been blocked.
```

Add the missing host to Embedding origins, serve the page over HTTPS on the standard port, and check the other pages around it. Changes to the list can take a couple of minutes to apply.

**The frame doesn't adjust its height, and links inside it don't open.** Your help center has a custom domain and the embed has no `customDomain`, or the page uses a plain `<iframe>` instead of the embed script.

**The frame shows "Help center not available."** JS-only access is on and your page sends no referrer. Change its `Referrer-Policy`.

**Your page's Content Security Policy blocks the embed.** Allow `https://helpcenter.io` in `script-src` and your help center's address in `frame-src`, plus your Login URL's origin on a private help center. See [Content Security Policy and allowed origins](https://developers.helpcenter.io/content/content-security-policy).

## Related

- [Content Security Policy and allowed origins](https://developers.helpcenter.io/content/content-security-policy)
- [Sign readers into the widget with a JWT](https://developers.helpcenter.io/content/widget-jwt)
- [Build your Login URL](https://developers.helpcenter.io/content/build-your-login-url)
