HTML Iframes
An iframe (inline frame) embeds another HTML document inside the current page, creating a nested browsing context with its own DOM, its own scripts, and its own navigation history. Iframes are how you embed a YouTube video, a Google Map, a payment widget, an ad, or a page from another domain without leaving your site. Because an iframe loads a completely separate document, it’s also one of the most security-sensitive elements in HTML, so understanding its attributes matters as much as understanding its syntax.
Overview / How it works
Every web page lives inside a "browsing context" — the environment that manages a document’s history, viewport, and script execution. Normally a whole browser tab is one browsing context. The iframe element creates a nested browsing context: a rectangular viewport inside the parent page that loads and renders an entirely independent document, complete with its own <html>, <head>, and <body>.
When the HTML parser encounters an iframe tag, it inserts an HTMLIFrameElement node into the DOM tree of the parent document, exactly like any other element — it participates in layout, can be styled (sizing, borders, positioning) from CSS, and can be selected with document.querySelector. But the moment the browser resolves the src attribute, it spins up a second, independent document inside that node. This child document has its own DOM, its own CSSOM, and, if it contains scripts, its own JavaScript execution environment. The two documents can only interact through a restricted API (mainly window.postMessage for cross-origin communication) unless they share the same origin, in which case scripts can reach across via iframe.contentWindow or iframe.contentDocument.
This isolation is deliberate. If an iframe loads a document from a different origin (different scheme, host, or port), the same-origin policy blocks the parent page’s scripts from reading or modifying anything inside it, and vice versa. That’s what makes it reasonably safe to embed a video player or a map widget you don’t control — the embedded page cannot read your page’s cookies, DOM, or local storage, and you cannot read its internal state either.
Semantically, an iframe is a replaced element: like an img or a form control, the browser draws its rendered content (the child document) into the box the element occupies, rather than rendering the tag’s own children as normal DOM content. The children of an iframe element only matter as fallback content for browsers old enough not to support iframes at all — effectively none exist today, so this fallback is rarely seen, but it must still be valid content and the tag must still be properly closed.
Syntax
<iframe
src="https://example.com/page.html"
title="Descriptive name of embedded content"
width="600"
height="400"
loading="lazy"
sandbox="allow-scripts"
allow="fullscreen"
>
<p>Your browser does not support iframes.</p>
</iframe>
| Attribute | Purpose |
|---|---|
src |
URL of the document to embed. Can be omitted and set later via JavaScript, or use srcdoc instead to embed inline HTML directly. |
title |
A short accessible name describing the embedded content. Screen readers announce this so users know what the frame contains; treat it as required in practice. |
width / height |
The size of the frame’s viewport in CSS pixels. Without them the frame defaults to roughly 300×150. Prefer CSS for responsive sizing in real projects. |
loading |
lazy defers loading offscreen iframes until the user scrolls near them; eager (default) loads immediately. |
sandbox |
Restricts what the embedded document is allowed to do. An empty sandbox="" applies every restriction (no scripts, no forms, no same-origin access, no top-level navigation, etc.); adding space-separated tokens like allow-scripts or allow-forms re-enables specific capabilities. |
allow |
Grants access to browser features/permissions policies for the embedded document, e.g. allow="camera; microphone; fullscreen". |
referrerpolicy |
Controls how much referrer information is sent when the frame requests its content. |
name |
Names the browsing context so a link with target="framename" elsewhere on the page can load its result inside this frame. |
Examples
Example 1: A basic embedded page
<iframe
src="https://www.example.com/"
title="Example.com homepage"
width="500"
height="300">
</iframe>
Result: The browser draws a 500×300-pixel rectangular box on the page. Inside that box, a completely separate, independently scrollable copy of example.com’s homepage renders, with its own default border in most browsers. The rest of your page is unaffected by anything happening inside that box.
This is the minimal working iframe: a src, an accessible title, and explicit dimensions. Without width/height, the box would default to a small, often-too-small size.
Example 2: A sandboxed, lazily-loaded embed with permissions
<iframe
src="https://player.example.com/video/42"
title="Product demo video"
width="640"
height="360"
loading="lazy"
sandbox="allow-scripts allow-same-origin allow-popups"
allow="fullscreen; autoplay">
</iframe>
Result: A 640×360 video player region appears. If this iframe starts far down the page, the browser will not request the video player’s document at all until the user scrolls close to it, saving bandwidth. Once loaded, the embedded player is permitted to run scripts, treat itself as same-origin for its own storage, open popups (e.g. a share dialog), request fullscreen, and autoplay — but nothing beyond those explicitly granted permissions.
This demonstrates defense in depth: sandbox locks the frame down by default and you opt back in only to the capabilities the embedded widget actually needs, while allow separately grants specific powerful browser features.
Example 3: A realistic embedded map with fallback content and a named target
<div>
<h3>Our Office Location</h3>
<iframe
src="https://maps.example.com/embed?q=1+Main+St"
title="Map showing our office at 1 Main St"
width="100%"
height="350"
loading="lazy"
referrerpolicy="no-referrer-when-downgrade">
<p>
A map could not be displayed. <a href="https://maps.example.com/?q=1+Main+St">View the location directly</a>.
</p>
</iframe>
</div>
Result: A heading reading "Our Office Location" appears above a full-width, 350-pixel-tall embedded map. Because the frame’s fallback content is a link to the same location on the map provider’s full site, on the vanishingly rare chance iframes aren’t supported (or the embed fails to load a document at all), the user still sees a working link instead of an empty box.
Notice the fallback paragraph and link live between the opening and closing iframe tags — valid, meaningful markup, not just a placeholder comment.
How it works step by step
- The HTML parser reaches the
<iframe>start tag and creates anHTMLIFrameElement, inserting it into the parent document’s DOM tree at that point, just like any other element. - The browser reads the
srcattribute and queues a navigation request for that URL in a brand-new, nested browsing context associated with the element. - Layout reserves a box for the iframe using its
width/height(or CSS), and ifloading="lazy"is set and the box is currently offscreen, the browser defers step 2 until the box nears the viewport. - When the response arrives, the browser parses it as a full, independent HTML document — building a separate DOM, CSSOM, and render tree, exactly as it would for a top-level page.
- The nested document is painted inside the reserved box. If it is cross-origin, the browser enforces the same-origin policy: parent and child cannot read each other’s DOM or JavaScript globals directly.
- If
sandboxis present, the nested browsing context runs with a restricted feature set (an opaque origin, no scripts, no form submission, etc., unless explicitly re-enabled by tokens) regardless of what the embedded document itself would normally be allowed to do. - The parent page’s own layout, scripts, and event loop continue running independently; only explicit communication via
postMessage, or same-origin DOM access, lets the two documents talk to each other.
Common Mistakes
Mistake 1: Forgetting to close the iframe tag
<iframe src="widget.html" title="Widget">
<p>Next section starts here.</p>
Because iframe is not a void element, the parser treats everything after the opening tag as fallback content inside the iframe until it finds a closing tag — here there is none, so the paragraph (and potentially the rest of the document) gets swallowed as fallback content instead of appearing as a normal sibling section. The fix is to always explicitly close it:
<iframe src="widget.html" title="Widget"></iframe>
<p>Next section starts here.</p>
Mistake 2: Embedding untrusted or third-party content with no restrictions
<iframe src="https://ads.thirdparty.example/slot/9" width="300" height="250"></iframe>
This works, but it hands the embedded document full default capabilities: it can run scripts, submit forms, open new windows, and (in same-origin cases) poke at your page. For any content you don’t fully control, add a sandbox attribute and grant back only what’s genuinely needed, plus a title for accessibility:
<iframe
src="https://ads.thirdparty.example/slot/9"
title="Advertisement"
width="300"
height="250"
sandbox="allow-scripts"
loading="lazy">
</iframe>
The sandboxed version can still run its ad script, but it cannot navigate the top-level page, submit forms, or access storage as if it shared your origin.
Best Practices
- Always include a descriptive
titleattribute — it’s the only way screen reader users learn what a frame contains. - Set explicit
widthandheight(or size it with CSS) to prevent layout shift while the embedded document loads. - Add
loading="lazy"for iframes that sit below the initial viewport, such as embedded videos or maps far down a long page. - Use the most restrictive
sandboxvalue that still lets the embed function, rather than omittingsandboxentirely for third-party content. - Prefer
allowto grant only the specific browser permissions (camera, fullscreen, autoplay) an embed actually needs. - Only embed content over HTTPS, and be cautious embedding pages you do not control inside pages that handle sensitive user data.
- Provide meaningful fallback content between the tags for the rare case the frame cannot load, instead of leaving it empty.
- Remember that CSS handles visual styling (borders, rounded corners, responsive sizing) — keep the iframe’s own attributes focused on behavior and security, not presentation.
Practice Exercises
- Write an iframe that embeds
https://www.wikipedia.org/at 500 pixels wide and 400 pixels tall, with an appropriatetitleand a fallback paragraph containing a plain link to the same URL. - Add a
sandboxattribute to your iframe from Exercise 1 that allows the embedded page to run scripts and treat itself as same-origin, but nothing else. Which sandbox tokens do you need? - Create a page with a link (
<a href="page2.html" target="preview">) and an iframe namedpreview(<iframe name="preview">). Explain in your own words what should happen when the link is clicked.
Summary
- An
iframeembeds an entirely separate HTML document as a nested browsing context inside the current page. - It behaves as a replaced element in layout; its children only serve as fallback content and it must always be explicitly closed.
- The same-origin policy isolates parent and child documents from each other unless they share an origin or use
postMessage. sandboxrestricts the embedded document’s capabilities by default, re-enabling only the tokens you specify;allowgrants specific browser feature permissions.loading="lazy"defers offscreen iframes to improve page performance.- Always give iframes a descriptive
title, explicit dimensions, and the least privilege necessary, especially for third-party content.
