Skip to contentnotionic
Docs menu

Guides

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 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 instead.

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