Iframe or custom element: how a third-party widget should arrive on your page
Should an embedded third-party widget be an iframe, or a script that defines a custom element?
Choose an iframe when isolation matters more than integration — untrusted content, a separate origin, a hard security boundary. Choose a custom element when the widget has to behave like part of the page: inherit the layout, size to its content, participate in one focus order, and be readable by a screen reader as a single document.
·9 min read
What does an iframe actually give you?
A separate document with a separate origin, which is the strongest isolation the web platform offers. The host page's CSS cannot reach in, the widget's CSS cannot reach out, the JavaScript realms are distinct, and same-origin policy stops the frame reading the host's DOM or cookies. If the embedded content is untrusted, this is not a preference — it is the correct answer.
Style isolation, absolutely. No selector on either side can affect the other. Nothing else on the platform is this complete.
A security boundary. With sandbox and a permissions policy the host constrains what the frame may do, and the frame cannot escalate.
Version independence. The vendor can rewrite everything inside the frame without touching the host page's markup.
What does an iframe cost?
Everything that depends on the widget being part of the page — which, for an interactive widget, is more than people expect. The costs are individually small and collectively decisive.
Height. A frame does not size to its content. Either you fix a height and live with the scrollbar, or you build a postMessage resize channel and maintain it — and it will still be wrong for one frame after every content change.
Anything that must escape the box. A dropdown, a date picker, a lightbox or a modal cannot render outside the frame's rectangle. Inside a short frame, a calendar popover is clipped by its own container.
Focus and scrolling. The frame is one tab stop from the outside and its own document within. Scroll anchoring, scroll-margin for a sticky header, and "scroll the page to this error" all stop working the way they do elsewhere.
Typography and theme. Nothing inherits. Font family, type scale and the host's dark mode all have to be re-sent into the frame and re-applied, and any mismatch reads as a foreign object dropped on the page.
Assistive technology. A frame is announced as a separate document. The heading structure inside it does not join the page's outline, which weakens exactly the linear reading that a screen-reader user relies on.
Crawlers and links. Content in a frame is a different URL. Whether and how it is associated with the host page is not something you control, and a link to a specific state inside the frame is not a link the host page can produce.
What does a custom element give you instead?
A real node in the page's own document. It takes part in normal layout, so it sizes to its content and reflows with the column it sits in. It shares one focus order with the rest of the page. Its headings join the page outline. A modal it opens can cover the viewport, because there is no rectangle to escape.
Illustrative markup. Two lines, no build step — a custom element is valid HTML in any framework or in a hand-edited file.
It also degrades in a more useful way. An element the browser does not recognise is an unknown element, not an error: it renders as an empty inline box until the script defines it, at which point it upgrades in place. There is no framework requirement on the host, and nothing to reconcile with the host's build tooling.
Does a custom element need shadow DOM?
It does not, and the decision is a genuine trade rather than a formality. A shadow root gives style encapsulation that is nearly as strong as an iframe's while keeping the element in the page's layout and focus order. That is the textbook answer, and for most widgets it is the right one.
It stops being the right one when the widget's own UI library injects its styles into document.head. A CSS-in-JS engine that appends a <style> element to the head is emitting styles that do not cross a shadow boundary in either direction — so a component mounted in a shadow root renders unstyled. The WPIntegrate components hit exactly this: they are built on Fluent UI, whose Griffel engine inserts into document.head, so they render in the page's own DOM rather than in a shadow root.
The cost of that choice has to be stated rather than hidden: host-page CSS can reach the widget. What contains it is the library's own resets and generated class names, plus per-instance styling controls that let the widget be matched to the host deliberately instead of accidentally. If you are choosing a widget, this is a fair thing to ask a vendor — and a vendor who cannot tell you which one they use has not thought about your page.
How do you decide which one you want?
Iframe
Custom element
Style isolation
Complete
Shadow root, or the library's own resets
Security boundary
Separate origin, sandboxable
Shares the page's origin and JS realm
Sizes to its content
No — fixed or postMessage
Yes, like any element
Popovers and modals
Clipped to the frame
Free of the page
Focus order
A separate document
One order with the page
Screen-reader outline
Announced separately
Part of the page outline
Inherits host theme
No, must be re-sent
Yes, and can be overridden
Take the iframe for content you do not control or do not trust, for anything handling payment details, and where a hard security boundary is the requirement rather than a nice-to-have.
Take the custom element for a widget that is meant to read as part of the page — a booking flow, a document browser, a team directory, a form — and where clipped popovers and a fixed height would be visible defects.
One test cuts through most of the argument: would a reader be surprised to learn this section of the page came from somewhere else? If the honest answer is yes, it should not be an iframe.
Questions people ask about this
Is an iframe more secure than a custom element?
Yes, for the host page — a frame is a separate origin, can be sandboxed, and cannot read the host's DOM or cookies. A custom element shares the page's origin and JavaScript realm, so embedding one is a statement of trust in the vendor, which is why the code should come from a host you have reason to trust.
Why do iframes need a postMessage resize script?
Because an iframe does not size to its content — the parent sets its height, and the parent cannot see inside it. The frame has to measure itself and message the height out, which is a channel someone has to write, load and maintain on both sides.
Do custom elements work outside React or Vue?
Yes. A custom element is part of the HTML platform, so it works in plain HTML with no build step and inside every mainstream framework. That is the practical reason to prefer one for a widget distributed to sites you do not control.
Do WPIntegrate components use shadow DOM?
No, and deliberately. They are built on Fluent UI, whose Griffel styling engine inserts its styles into document.head, and those styles do not cross a shadow boundary — so a shadow-mounted component would render unstyled. They render in the page's DOM instead, with Fluent's own resets and generated class names limiting what host CSS can disturb.
Still the wrong answer for your case?
Then the case is worth hearing.
These articles describe the general shape. Tenants differ, policies differ, and the
interesting questions are the ones where the general shape does not fit.