Paste embed code to see what it renders, logs and loads
Paste the embed code a service gave you, usually a
<div> plus a <script> tag or
an <iframe>, and it runs in a sandboxed page
below. The preview renders at desktop, tablet or phone width, and
the report lists which elements stayed empty, every console message
and uncaught error, and each script, image and API call with its
status. The markup is checked too, for http:// URLs, relative
paths, integrity attributes that cannot work and scripts that start
before their element exists. Everything runs in your browser; the
code is not sent to our server.
Paste embed code to see what it renders, logs and loads
Put the element part, such as the <div> or
<iframe>, inside <body> at the
spot where the widget should appear. Put the script below it or just
before </body>; a script with
defer can also go in <head>, because
it waits until the page has been parsed. With several widgets on one
page, include the script once: widget scripts usually look for every
matching element when they start. In a CMS, use a Custom HTML or
Embed block, because the regular text editor often strips script
tags.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Our apartment</title>
<!-- A script with defer may also go here -->
</head>
<body>
<h1>Our apartment</h1>
<!-- The widget renders where its element is -->
<div data-widget="gallery" data-id="123"></div>
<!-- Once per page, below the elements it fills -->
<script src="https://cdn.example.com/widget.js" defer></script>
</body>
</html> Your code becomes the body of a page that loads in an iframe with the sandbox attribute and without allow-same-origin, so it gets a unique origin and cannot read this site's cookies, storage or page. Before your code runs, a small monitor script wraps console, fetch and XMLHttpRequest, listens for failed loads, including inside shadow roots, and reads the Resource Timing list. Each time the page changes, and once a second for the first 20 seconds, it measures every top-level element: its size, whether it has a shadow root and how many nodes it holds. An element counts as rendered once it has a width, a height and something inside it. The markup check parses your code with DOMParser, which never runs scripts or loads images.
Because of the sandbox, requests from the frame carry
Origin: null, document.cookie and
localStorage throw a SecurityError, and relative URLs
resolve against this page. A widget that relies on any of these
behaves differently here. The Test page download runs the same code
on a real origin once you serve it locally.
| Script widget (element + script) | Iframe | |
|---|---|---|
| Runs in | Your page, with your DOM, cookies and CSS | A separate page inside a frame |
| Blocked by | Your Content-Security-Policy (script-src, connect-src, img-src) | The framed page's X-Frame-Options or frame-ancestors, and your frame-src |
| Styling | Inherits your CSS unless it renders into a shadow root | Isolated from your CSS |
| Height | Grows with its content | Fixed unless the page reports its height to a resize script |
| Fails when | The script or its API calls are blocked, or it starts before its element exists | The page refuses to be framed or is served over http:// |
The preview sends no Content-Security-Policy. If your site sends one, it has to list the script's host in script-src, its API in connect-src and its images in img-src. Your CSS also reaches a widget that does not use a shadow root, so your fonts, resets and box-sizing rules change how it looks. Some vendors tie a key to a domain and reject requests from any other origin. In a single-page app, React, Vue or another framework may mount the element after the script has run, or re-render it later, and the widget then finds nothing or loses its content; call the widget's own init or scan function after the element is mounted.
# The script: expect 200 and a JavaScript content type, not text/html from an error page
curl -sS -o /dev/null -w "%{http_code} %{content_type}\n" https://cdn.example.com/widget.js
# The widget's API, asked the way a browser on your site asks it
curl -sS -o /dev/null -D - -H "Origin: https://yoursite.example" https://api.example.com/widget/123 \
| grep -i "^access-control-allow-origin" The second command must print * or exactly your origin. If it prints nothing, browsers block the call even though curl gets the data.
// npm i -D playwright && npx playwright install chromium
import { chromium } from "playwright";
const snippet = `<div id="widget"></div>
<script src="https://cdn.example.com/widget.js" defer></script>`;
const browser = await chromium.launch();
const page = await browser.newPage();
const problems = [];
page.on("console", (m) => m.type() === "error" && problems.push(m.text()));
page.on("pageerror", (e) => problems.push("Uncaught " + e.message));
page.on("requestfailed", (r) => problems.push(`${r.failure()?.errorText} ${r.url()}`));
page.on("response", (r) => r.status() >= 400 && problems.push(`${r.status()} ${r.url()}`));
// Serve the snippet on a made-up https origin, so CORS and cookies behave as on a live site
await page.route("https://test.example/", (route) =>
route.fulfill({ contentType: "text/html", body: `<!doctype html><body>${snippet}</body>` }));
await page.goto("https://test.example/");
const rendered = await page
.waitForFunction(() => document.querySelector("#widget")?.getBoundingClientRect().height > 0, null, { timeout: 10000 })
.then(() => true, () => false);
console.log(rendered ? "rendered" : "empty after 10 s", problems);
await browser.close(); Run it in CI against every page that carries the widget, so a vendor change that breaks rendering fails the build.
The sandbox attribute gives the preview frame a unique origin, so the code cannot reach webdevutility.com's cookies, storage or page. In return, requests carry Origin: null, document.cookie and localStorage throw a SecurityError, and relative URLs resolve against this page. Opening the downloaded test page as a file:// URL has the same null origin; serve its folder with npx serve or python3 -m http.server to get a real one.
Errors thrown inside a script from another origin reach the page as "Script error." with no message or line, because browsers hide them unless the script tag has crossorigin="anonymous" and the server sends CORS headers. Inside the sandbox every external script counts as another origin. DevTools always shows the full error.
This tool follows the specifications below. Read them when you need the exact normative wording, for example to settle an edge case between two implementations.
Put the element, such as the <div> or <iframe>, inside <body> where the widget should appear, and the <script> tag below it or just before </body>. A script with defer can also go in <head>. Include the script once, even for several widgets on a page. In WordPress, use a Custom HTML block; most site builders have a similar Embed or Code block.
Usually the script never ran, or it ran and found nothing to fill. Your page's Content-Security-Policy, an ad blocker or mixed content can block it. A script placed above its element without defer runs before that element exists. Or the element's id or data attributes do not match what the script looks for. The tool shows these as a failed request in Network, an error in Console or an element marked Empty.
A script embed runs in your page: it reads your DOM, inherits your CSS unless it uses a shadow root, and calls its API with CORS, so your Content-Security-Policy must allow it. An iframe embed loads a separate page in a frame, isolated from your CSS and scripts, and the framed page's X-Frame-Options or frame-ancestors header decides whether it may appear on your site at all.
The code runs in an iframe with the sandbox attribute and no allow-same-origin, so it gets a unique origin. It cannot read this site's cookies, storage or page, and it cannot navigate this tab. Its scripts and images load straight from their own servers into your browser, and the code is not sent to our API.
The preview has no Content-Security-Policy and almost no CSS. If your site sends a CSP, add the script's host to script-src, its API to connect-src and its image host to img-src. Also check that your CSS does not restyle the widget, that the vendor allows your domain, and that a framework such as React does not mount the element after the script has run.
The sandbox gives the code a unique origin. Requests carry Origin: null, so an API that only accepts listed domains refuses them, and document.cookie and localStorage throw a SecurityError. Relative URLs also resolve against webdevutility.com. Download the Test page and serve it from a local server to run the same code on a real origin.
Check whether the page in an iframe src allows framing with the iframe checker, tidy a one-line snippet with the HTML formatter, or escape it for a tutorial with the HTML encoder.