{"success":true,"data":{"block":{"id":"iframe-embed","name":"Iframe Embed","title":"Iframe Embed","category":"Advanced","categorySlug":"advanced","description":"Embeds a third-party page the site owner supplied — a map, reservation or booking widget, event calendar, ticketing page, online ordering storefront, or an externally hosted video — inside the standard section chrome. Lazy-loads by default and offers aspect-ratio, fixed-height, or full-screen containment. Only use it when the user actually pasted embed code or an embed URL.","thumbnail":{"desktop":"https://cdn.ing/assets/i/r/316266/ave5ucg3kc44zijjcb9ssh3rhpoc/iframe-embed-desktop.jpg","mobile":"https://cdn.ing/assets/i/r/316271/1qxs9nslqutvl1cudh4qyc0ne39y/iframe-embed-mobile.jpg"},"componentPath":"blocks/advanced/iframe-embed.tsx","code":"import { IframeEmbed } from \"@opensite/ui/blocks/advanced/iframe-embed\";\n\n// Parsed form: take the `src` out of the owner's embed code and put it in\n// `embedUrl` verbatim. `embedTitle` is the frame's accessible name (required\n// whenever `embedUrl` is set).\nexport default function Demo() {\n  return (\n    // Showcase-only: `loadingStrategy=\"eager\"` is set so this static preview\n    // paints the frame without waiting on the viewport observer. On a real page\n    // omit the prop — it defaults to \"lazy\" — and use \"eager\" only when the\n    // embed is above the fold.\n    <IframeEmbed\n      title=\"Find Us\"\n      subtitle=\"Visit\"\n      embedUrl=\"https://www.openstreetmap.org/export/embed.html?bbox=-87.6360%2C41.8760%2C-87.6120%2C41.8900&layer=mapnik\"\n      embedTitle=\"Map showing the restaurant location\"\n      loadingStrategy=\"eager\"\n      containMode=\"aspect-ratio\"\n      aspectRatio=\"16:9\"\n      referrerPolicy=\"strict-origin-when-cross-origin\"\n      iframeClassName=\"rounded-2xl border border-border\"\n    />\n  );\n}","propsSchema":{"embedUrl":{"type":"string","description":"URL of the page to embed in an iframe. Copy the `src` value out of the\nembed code the site owner supplied, EXACTLY as given. Must be an absolute\nhttps URL. Never invent, shorten, or \"correct\" this URL.\n\nOnly `http:` and `https:` URLs are rendered — anything else (`javascript:`,\n`data:`, protocol-relative, relative) is treated as if no URL were supplied,\nbecause an iframe `src` executes in this document's origin.","required":false},"embedHtml":{"type":"string","description":"Raw embed markup escape hatch, used only when the third party's snippet\ncannot be expressed as a single iframe URL. Paste the owner's markup\nverbatim.\n\nSECURITY: this markup is inserted verbatim and CAN execute. Bare `<script>`\ntags do not run (innerHTML semantics), which is why script-based widgets\nstill belong in `advanced/script-embed` — but inline event handlers\n(`onerror`, `onload`) and `<iframe srcdoc>` DO run, same-origin. Treat this\nprop as owner-trusted code, the same trust class as\n`script-embed`'s `inlineScriptHtml`; never populate it from scraped,\nmodel-authored, or otherwise non-owner content.\n\nWhen `embedUrl` is also present, `embedUrl` wins and this value is ignored.","required":false},"embedTitle":{"type":"string","description":"Accessible name for the iframe (rendered as the `title` attribute).\nRequired whenever `embedUrl` is set — screen readers announce it as the\nframe's label. Describe the embedded content, e.g. \"Online reservation form\".","required":false,"maxLength":120},"allowAttributes":{"type":"string","description":"Value for the iframe `allow` attribute (feature policy), copied from the\nowner's embed code, e.g. \"accelerometer; encrypted-media; picture-in-picture\".\nOmit when the embed code did not include one.","required":false},"sandboxAttributes":{"type":"string","description":"Value for the iframe `sandbox` attribute, e.g.\n\"allow-scripts allow-same-origin allow-forms\". When omitted the attribute is\nnot rendered at all (an empty sandbox attribute blocks nearly everything).","required":false},"referrerPolicy":{"type":"string","description":"Value for the iframe `referrerpolicy` attribute.","typeLabel":"\"no-referrer\" | \"no-referrer-when-downgrade\" | \"origin\" | \"origin-when-cross-origin\" | \"same-origin\" | \"strict-origin\" | \"strict-origin-when-cross-origin\" | \"unsafe-url\"","enum":["no-referrer","no-referrer-when-downgrade","origin","origin-when-cross-origin","same-origin","strict-origin","strict-origin-when-cross-origin","unsafe-url"],"required":false},"loadingStrategy":{"type":"string","description":"\"lazy\" (default) defers mounting the embed until it is within 200px of the\nviewport and also sets the native `loading=\"lazy\"` attribute. Use \"eager\"\nonly for an embed that is above the fold on first paint.","typeLabel":"\"lazy\" | \"eager\"","enum":["lazy","eager"],"required":false},"containMode":{"type":"string","description":"How the embed is sized. \"aspect-ratio\" (default) keeps a responsive ratio,\n\"fixed-height\" pins a pixel height (use for forms and calendars that scroll\ninternally), \"full-screen\" fills the viewport edge to edge.","typeLabel":"\"aspect-ratio\" | \"fixed-height\" | \"full-screen\"","enum":["aspect-ratio","fixed-height","full-screen"],"required":false},"aspectRatio":{"type":"string","description":"Ratio used when `containMode=\"aspect-ratio\"`. Use \"custom\" together with\n`customAspectRatio` for anything not in this list. A value outside this\nlist falls back to \"16:9\" rather than collapsing the frame.","typeLabel":"\"16:9\" | \"4:3\" | \"1:1\" | \"9:16\" | \"21:9\" | \"custom\"","enum":["16:9","4:3","1:1","9:16","21:9","custom"],"required":false},"customAspectRatio":{"type":"string","description":"CSS aspect-ratio value used when `aspectRatio=\"custom\"`, e.g. \"5 / 4\".\nBlank or missing falls back to 16:9.","required":false},"fixedHeight":{"type":"string","description":"CSS height used when `containMode=\"fixed-height\"`, e.g. \"640px\".\nDefaults to \"600px\". A unitless value (\"640\") is read as pixels.","required":false},"iframeClassName":{"type":"string","description":"Additional CSS classes applied to the iframe element (parsed `embedUrl`\npath) or to the wrapper around the pasted markup (`embedHtml` path).\n\nKeep these to utilities the library already uses: live-site CSS is\nsafelist-compiled from `className`-keyed block props plus the class strings\nshipped in `dist`, so a one-off utility written only into this prop can be\nmissing from the customer stylesheet. Mirror anything exotic into\n`className` (which IS collected) instead.","required":false},"emptyStateLabel":{"type":"string","description":"Short line shown in place of the embed when no `embedUrl`/`embedHtml` is\nconfigured yet. With no embed and no label the block renders no content at\nall — it never fabricates a placeholder frame.","required":false},"sectionId":{"type":"string","description":"Optional Section ID.","required":false},"title":{"type":"string","description":"Optional heading rendered above the embed by the Section.","required":false},"subtitle":{"type":"string","description":"Optional kicker rendered above the heading by the Section.","required":false},"background":{"type":"string","description":"Section background variant.","typeLabel":"SectionBackground","enum":["default","white","gray","dark","gradient","primary","secondary","transparent","muted"],"required":false},"spacing":{"type":"string","description":"Section spacing variant. Defaults to \"none\" when\n`containMode=\"full-screen\"` so the embed reaches the viewport edges.","typeLabel":"SectionSpacing","enum":["none","sm","md","lg","xl","hero"],"required":false},"pattern":{"type":"string","description":"Pattern background key.","typeLabel":"PatternName","enum":["squareAltGrid","grid1","noise","dotPattern","dotPattern2","circles","waves","crossPattern","architect","tinyCheckers","p6","dots","circuitBoardBasic","circuitBoardFadeTop","circuitBoardFadeBottom","circuitBoardFadeCenter","circuitBoardFadeTopLeft","circuitBoardFadeTopRight","circuitBoardFadeBottomLeft","circuitBoardFadeBottomRight","dashedGridBasic","dashedGridFadeTop","dashedGridFadeBottom","dashedGridFadeCenter","dashedGridFadeTopLeft","dashedGridFadeTopRight","dashedGridFadeBottomLeft","dashedGridFadeBottomRight","diagonalCrossBasic","diagonalCrossFadeTop","diagonalCrossFadeBottom","diagonalCrossFadeCenter","diagonalCrossFadeTopLeft","diagonalCrossFadeTopRight","diagonalCrossFadeBottomLeft","diagonalCrossFadeBottomRight","gridBasic","gridFadeTop","gridFadeBottom","gridFadeCenter","gridFadeTopLeft","gridFadeTopRight","gridFadeBottomLeft","gridFadeBottomRight","gridDotsBasic","gridDotsFadeCenter","gradientGlowTop","gradientGlowBottom","spotlightLeft","spotlightRight","radialGradientTop","radialGradientBottom"],"required":false},"patternOpacity":{"type":"number","description":"Pattern opacity (0-1).","required":false},"className":{"type":"string","description":"Additional CSS classes for the section.","required":false},"containerClassName":{"type":"string","description":"Additional CSS classes for the container.","required":false},"style":{"type":"object","description":"Inline styles for the section element.","typeLabel":"React.CSSProperties","required":false}},"exampleProps":{"title":"Find us","embedUrl":"https://www.openstreetmap.org/export/embed.html?bbox=-87.6360%2C41.8760%2C-87.6120%2C41.8900&layer=mapnik","embedTitle":"Map showing the restaurant location","aspectRatio":"16:9","loadingStrategy":"lazy","referrerPolicy":"strict-origin-when-cross-origin"},"dependencies":["@opensite/ui"],"tags":["iframe","embed","widget","third-party","calendar","map","video-embed","shop","booking","reservation","ticketing","external-content","integration","advanced"],"performance":{},"importantUsageNotes":"Use ONLY when the site owner supplied an iframe embed (map, reservation widget, calendar, storefront, ticketing, or a third-party video host). VERBATIM CODE ONLY: copy the embed code, URL, and ids EXACTLY as the site owner supplied them — never invent, guess, shorten, complete, or 'correct' them. Never use this block unless the user actually supplied the embed. If no embed code was provided, choose a different block or leave the section out. Prefer the parsed form: put the embed's src into embedUrl and leave embedHtml empty — embedUrl is lazy-loadable, containment-controllable and auditable. embedUrl must be an absolute http(s) URL; any other scheme is ignored and the block renders its empty state. Use embedHtml only when the owner's snippet cannot be reduced to one iframe URL, and treat it as owner-trusted CODE, not inert markup: bare <script> tags do not run, but inline event handlers and <iframe srcdoc> do execute same-origin — script-based widgets still belong in advanced/script-embed. embedTitle is required whenever embedUrl is set (screen readers announce it as the frame's name). Leave sandboxAttributes unset unless the owner's code included one — an empty sandbox attribute blocks almost everything. Choose containMode=\"fixed-height\" for forms and calendars that scroll internally, \"full-screen\" only for a page whose entire purpose is the embed. This block renders third-party content, not media-library assets. It declares no media slots; do not route images, logos, or videos into its props.","usageRequirements":{"requiredProps":[],"propConstraints":{"embedUrl":{"note":"Absolute http(s) URL taken verbatim from the owner's embed code. Wins over embedHtml when both are present. Any other scheme (javascript:, data:, protocol-relative, relative) is ignored and the block falls back to embedHtml or its empty state."},"embedHtml":{"note":"Raw markup escape hatch, pasted verbatim from the owner. It is owner-trusted CODE, not inert markup: bare <script> tags do not run, but inline event handlers and <iframe srcdoc> execute same-origin — never build this value from scraped or generated content. Only use when the embed cannot be expressed as a single embedUrl."},"embedTitle":{"maxLength":120,"note":"REQUIRED whenever embedUrl is set. Accessible name for the frame, e.g. 'Map showing the restaurant location'."},"aspectRatio":{"note":"One of \"16:9\", \"4:3\", \"1:1\", \"9:16\", \"21:9\", or \"custom\" (with customAspectRatio). Any other value is ignored and the block renders 16:9."},"iframeClassName":{"note":"Optional utility classes for the embed element (or, on the embedHtml path, its wrapper). Live-site CSS is safelist-compiled, and this key is NOT scanned for class tokens — stick to utilities the library already ships, or put the classes in className instead, which IS collected."},"allowAttributes":{"note":"Copy the `allow` attribute from the owner's embed code, or omit."},"sandboxAttributes":{"note":"Copy the `sandbox` attribute from the owner's embed code, or omit entirely. Never emit an empty string."},"customAspectRatio":{"note":"Only meaningful when aspectRatio=\"custom\". A CSS aspect-ratio value such as \"5 / 4\"."},"fixedHeight":{"note":"Only meaningful when containMode=\"fixed-height\". A CSS length such as \"640px\"; a unitless value like \"640\" is read as pixels. Defaults to \"600px\"."},"emptyStateLabel":{"maxWords":12,"note":"Short honest line shown when no embed is configured. Omit it and the block renders nothing rather than a fake frame."}},"mediaSlots":{},"requiresSiteCapabilities":[],"notes":["VERBATIM CODE ONLY: copy the embed code, URL, and ids EXACTLY as the site owner supplied them — never invent, guess, shorten, complete, or 'correct' them. Never use this block unless the user actually supplied the embed. If no embed code was provided, choose a different block or leave the section out.","This block renders third-party content, not media-library assets. It declares no media slots; do not route images, logos, or videos into its props.","Supply either embedUrl or embedHtml. With neither, the block renders no embed.","Set embedTitle whenever embedUrl is set; an untitled iframe is an accessibility failure.","embedUrl must be an absolute http(s) URL; other schemes are ignored and no frame is rendered."]}}},"timestamp":"2026-08-27T08:34:31.536Z"}