# Live embeds

> Load embeds of sites you trust in a sandboxed iframe.

Embeds render as preview cards by default: many sites refuse to be framed, and an embedded page runs its own scripts. Pass `embeds` to load the sites you trust live, in a sandboxed iframe with the caption below.

```typescript
<NotionRenderer blocks={blocks} embeds={["codepen.io", "figma.com", "loom.com"]} />
```

## Choose which sites load

`embeds` takes an `EmbedPolicy`, exported from `notionic`:

- **A list of hosts.** A listed host includes its subdomains, so `figma.com` matches `www.figma.com` but not `notfigma.com`. Hosts match ignoring case.
- **`true`** loads every embed.
- **A function** gets the embed's URL and returns whether it loads:

```typescript
<NotionRenderer
  blocks={blocks}
  embeds={(url) => url.startsWith("https://codepen.io/your-name/")}
/>
```

It's off by default, so embeds stay cards until you opt in. Only http(s) URLs are ever framed, and an embed of `…/demos/<name>` that matches one of your [Interactive demos](https://notionic.dev/docs/demos) renders the demo instead.

## The frame

The iframe is 16:9 with the same outline as a YouTube video. To set its height instead, add `?height=` in pixels to the embed URL, as for demos: with `embeds={["sanding.space"]}`, an embed of `https://www.sanding.space/demos/surface?height=600` renders 600px tall. Heights are whole numbers from 1 to 9999; anything else keeps 16:9. The URL isn't rewritten, so `height` also reaches the embedded page, which most sites ignore.

It loads lazily and sends a strict referrer. Its title is the caption, or the page's title from `fetchBlocks`. It's sandboxed to allow scripts, forms, popups and presentation, which most embeds need to work.

## Fit the embedded page's height

A fixed height clips content that grows, or that wraps taller on a phone. If you run the embedded site, let it report its own height: post `{ type: "notionic:resize", height }` to the parent, and the frame becomes that many pixels tall.

```javascript
new ResizeObserver(([entry]) => {
  const height = Math.ceil(entry.borderBoxSize[0].blockSize)
  window.parent.postMessage({ type: "notionic:resize", height }, "*")
}).observe(document.documentElement)
```

- **Starting size.** `?height=`, or 16:9, holds until the first message.
- **Heights** are rounded, at least 1px and at most 9999px. Anything that isn't a positive number is ignored.
- **Who can resize.** A message counts only from one of the page's live embed frames, and from an origin `embeds` allows. With a host list, subdomains count too, so a `sanding.space` embed that redirects to `www.sanding.space` still resizes. A function can't run in the browser, so with one only the embed URL's own origin counts: embed a redirecting URL by its final address.
- **What it costs.** A page with live embeds ships a small listener for these messages; pages without them ship nothing new. Demos aren't iframes, so they don't use it.

## Don't list your own site

A page framed from your own origin with both scripts and same-origin access can lift its sandbox. Embed your own components as [Interactive demos](https://notionic.dev/docs/demos) instead.

A site that refuses to be framed shows an empty box, so try an embed before you list its host.
