HTML Iframe Element
Think of a window in a house. Through it you see a garden that is not part of the house, and that you do not own, but you can look at it, and it appears right where you want it.
The <iframe> (inline frame) is that window for the web. It shows a whole other web page inside your page: a map, a video player, a payment form, a booking widget, a code demo, or even another section of your own site.
It is the most common embedding tool, and also the one that needs the most care, because the page in the window is not yours.
What You Will Learnβ
- Embedding a page with
src, and writing a page inline withsrcdoc - Naming and sizing frames, and making them responsive
- Lazy loading,
allow, andreferrerpolicy - The
sandboxattribute, step by step - Why some pages refuse to be framed
- Talking to a frame with
postMessage - Accessibility, performance, and common mistakes
The Basicsβ
- HTML Code
- Browser Output
<iframe
src="https://example.com/booking"
title="Book a table"
width="600"
height="400"
loading="lazy"
></iframe>
<!-- Or write the small page directly with srcdoc -->
<iframe
title="A greeting"
srcdoc="<h3>Hello from inside the frame</h3><p>I am a separate page.</p>"
sandbox
></iframe>
An <iframe> has its own document: its own HTML, CSS, JavaScript, and history. Your CSS does not leak in, and its CSS does not leak out. That is the great strength of frames, and the reason they are used for third-party widgets.
Attributesβ
| Attribute | What it does | Advice |
|---|---|---|
src | The address of the page to show | Use https:// |
srcdoc | HTML written right in the attribute, shown instead of src | Good for demos and tiny pages |
title | A name for the frame | Always add it |
width, height | Size, in pixels, with no units | Or use CSS |
loading | lazy waits until the frame is near the screen | Use lazy for anything below the first screen |
sandbox | Removes permissions from the framed page | Use it for anything you do not fully control |
allow | Grants features such as fullscreen or camera | Grant only what is needed |
allowfullscreen | Lets the frame go full screen | Common for video players |
referrerpolicy | What address the framed site is told you came from | Limit it for privacy |
name | A name that links and forms can target | Rarely needed |
Text or elements inside the <iframe> tags are shown only by browsers that cannot display frames, which today is almost none. You can put a fallback link there, but do not depend on it:
<iframe src="https://example.com/booking" title="Book a table">
<a href="https://example.com/booking">Open the booking page</a>
</iframe>
Name Every Frameβ
A title is required for accessibility. Screen reader users often move through a page by listing its frames, and an untitled frame is announced only as "frame".
<!-- Bad -->
<iframe src="https://player.example.com/embed/42"></iframe>
<!-- Good -->
<iframe src="https://player.example.com/embed/42"
title="Interview with a baker (video, 12 minutes)"></iframe>
A good title says what it holds, and for media, the kind and length. Keep it short. It is the name of the frame, not a replacement for a text alternative.
Size and Responsive Framesβ
A frame with no size gets a small default, and you may need to set both width and height. For a fluid layout, give it a shape in CSS:
iframe {
display: block;
width: 100%;
max-width: 48rem;
aspect-ratio: 16 / 9;
border: 0;
}
This is the same technique you saw for video in Responsive Video. It also stops the page jumping while the frame loads.
Tips:
- Use
border: 0(orstyle="border:0") to remove the default frame border. - Do not rely on a fixed height for content that changes, such as a form. It may show a scroll bar inside the frame, which is hard to use. Either make the frame tall enough, or let the framed page tell you its height with
postMessage(see below). - At 200% zoom and on phones, check that the framed content is readable without sideways scrolling inside the frame.
Lazy Loadingβ
Each frame loads a whole page, with its own scripts and styles. A page with three embeds can easily double in weight.
<iframe src="β¦" title="β¦" loading="lazy"></iframe>
loading="lazy" tells the browser to wait until the visitor scrolls near the frame. Do not use it on a frame that is visible at the top of the page.
For the heaviest players, use a click-to-load pattern (sometimes called a facade): show a lightweight picture and a button, and create the iframe only when the visitor clicks.
<button type="button" id="load-map">Show the map</button>
<div id="map-slot"></div>
document.getElementById("load-map").addEventListener("click", () => {
const frame = document.createElement("iframe");
frame.src = "https://maps.example.com/embed?q=Town+Hall";
frame.title = "Map of the Town Hall";
frame.loading = "lazy";
frame.style.cssText = "width:100%;aspect-ratio:16/9;border:0";
document.getElementById("map-slot").append(frame);
});
This also protects your visitors' privacy, because nothing is requested from the third party until they ask for it.
The Sandboxβ
The sandbox attribute is the most important safety tool. With a plain sandbox (empty), the framed page is locked down: it cannot run scripts, submit forms, open pop-ups, use storage, or reach your page. You then add back only what it needs, with space-separated keywords.
| Keyword | Gives back |
|---|---|
allow-scripts | Running JavaScript |
allow-same-origin | Treated as its real origin (so storage and cookies work) instead of an anonymous one |
allow-forms | Submitting forms |
allow-popups | Opening new windows with window.open or target="_blank" links |
allow-popups-to-escape-sandbox | Pop-ups that are not themselves sandboxed |
allow-modals | alert, confirm, and print |
allow-top-navigation-by-user-activation | Navigating your whole page, but only after a click |
allow-downloads | Starting downloads |
Try It: See What a Sandbox Doesβ
This frame holds a small page that tries three things: running a script, using storage, and reading this page's title. Turn the permissions on and off.
- HTML Code
- Browser Output
<!-- Locked down: nothing runs -->
<iframe src="/page.html" title="Locked down" sandbox></iframe>
<!-- Scripts allowed, but kept in an anonymous origin -->
<iframe src="/page.html" title="Scripts only" sandbox="allow-scripts"></iframe>
<!-- A typical widget from a company you trust -->
<iframe
src="https://widget.example.com/"
title="Support chat"
sandbox="allow-scripts allow-same-origin allow-forms allow-popups"
></iframe>
<iframe sandbox="" β¦></iframe>The sandbox removes everything you do not list. Switch the boxes and watch what the frame can and cannot do.
allow-scripts together with allow-same-origin, on a page from your own origin, lets that page reach into its parent and remove the sandbox attribute. The sandbox then does nothing. Use both only for content hosted on a different origin that you have chosen to trust, such as a widget from a vendor. The demo above shows the reason: with both on, the framed page can read this page.
allow and referrerpolicyβ
allowβ
Powerful features such as the camera, microphone, geolocation, and fullscreen are off by default inside a frame from another site. Grant the ones the embed really needs:
<iframe
src="https://player.example.com/embed/42"
title="Interview with a baker"
allow="fullscreen; picture-in-picture"
></iframe>
Never write allow="camera; microphone; geolocation" out of habit. Each permission is also a risk for your visitors.
referrerpolicyβ
When a frame loads, the other site normally learns the address of the page that embedded it. Limit that:
<iframe src="β¦" title="β¦" referrerpolicy="strict-origin-when-cross-origin"></iframe>
Use no-referrer when the other site does not need to know at all. See Third-Party Content Security.
When a Site Refuses to Be Framedβ
Many sites forbid being shown in a frame, to stop "clickjacking" (tricking people into clicking something hidden under a harmless-looking page). They send a header such as:
X-Frame-Options: DENY
Content-Security-Policy: frame-ancestors 'self'
If you try to embed such a page, you will see an error such as "refused to connect", or a blank box. You cannot override this from your side. Use the site's official embed address or widget (many services publish one), or link to the page instead.
The reverse is also true: you can protect your own pages from being framed by other sites:
Content-Security-Policy: frame-ancestors 'self' https://partner.example.com
Talking to a Frame: postMessageβ
A frame from another origin is sealed: your scripts cannot read it, and it cannot read yours. But the two sides can send each other messages, which is how a widget can tell your page its height, or that a form was submitted.
const frame = document.querySelector("iframe");
const trusted = "https://widget.example.com";
window.addEventListener("message", (event) => {
if (event.origin !== trusted) return; // ignore everyone else
if (event.data && event.data.type === "height") {
frame.style.height = Number(event.data.value) + "px";
}
});
frame.contentWindow.postMessage({type: "hello"}, trusted);
window.parent.postMessage(
{type: "height", value: document.documentElement.scrollHeight},
"https://yourshop.example.com"
);
The two rules that keep this safe:
- Always check
event.originbefore trusting a message. - Always name the target origin in
postMessage(never"*"for anything private).
Accessibility at a Glanceβ
- Give every frame a
titlethat says what it holds. - Make sure keyboard users can Tab in and out. Do not trap focus. Test with the embed.
- Avoid scroll bars inside frames when you can. Size the frame to the content.
- Check zoom and small screens: the framed content needs to reflow, and you cannot fix its CSS.
- Provide a text alternative or link to the content outside the frame (an address for a map, a transcript for a video, a booking link).
- Do not hide essential information in a frame that may fail to load or be blocked.
- Do not nest frames inside frames inside frames. Screen reader users get lost.
- Respect the embedded page's own accessibility: you inherit its problems.
Performance Notesβ
- Each frame is a whole page. It costs a connection, scripts, styles, and memory.
- Use
loading="lazy", and click-to-load for heavy players. - Fewer frames is better. Combine widgets where you can, and remove those no one uses.
- Set a size so the layout does not jump.
- Test on slow connections and old phones.
Security and Privacy Notesβ
- A frame is someone else's page in your page. It can run code, set cookies, and track visitors. Restrict it with
sandbox,allow, andreferrerpolicy. - Use
https://for every frame. An insecure frame in a secure page is blocked, or can be tampered with. - Never put user input straight into
srcorsrcdoc. A visitor could point your frame at a harmful address, or inject markup. Use an allow-list of addresses, and buildsrcdoconly from trusted text. - Defend your own pages with
frame-ancestors(orX-Frame-Options) so no one can frame them for clickjacking. - Check
event.originon everypostMessage. - Ask for consent before loading trackers, in places where the law requires it. Click-to-load helps.
- Never put secrets, such as API keys, in a frame's address or in
srcdoc. - The full set of defences is in Third-Party Content Security.
Global Audience Notesβ
- Frames are heavy for slow or metered connections. Use lazy loading and click-to-load.
- Some services are blocked or slow in some countries. Always offer a plain link or a text alternative.
- Check that the embedded content supports your visitors' languages, and pass a language where the service allows it (many embeds take a parameter such as
hlorlang). - Right-to-left pages: check that the framed content mirrors correctly, and use logical CSS properties around it.
- Privacy and cookie laws differ by region. Load third-party frames only after consent where it is required.
- Do not assume a fixed width. Phones in many regions are small, and zoom is common.
Common Mistakes to Avoidβ
- No
title. - No size or
aspect-ratio, so the page jumps, or the frame is tiny. - No
loading="lazy"on frames below the fold. - No
sandboxon content you do not control. allow-scriptsandallow-same-origintogether on your own content.- Granting
allowpermissions the embed does not need. - Using
http://addresses. - Trying to embed a site that forbids framing, and not providing a fallback link.
- Trusting any
postMessagewithout checkingevent.origin. - Building
srcfrom user input. - Nested frames, and scroll bars inside scroll bars.
- Putting the only copy of important information inside a third-party frame.
Practice Timeβ
Try these in your editor:
- Embed a small page with
srcdoc, and give the frame atitle. - Add
sandbox(empty), and put ascriptin the page. See that it does not run. - Add
allow-scripts, and see that it runs. - Add
allow-same-origin, and read the parent's title from the framed script. Then remove it again. - Give the frame an
aspect-ratio, and resize your window. - Add
loading="lazy", and check the network panel while scrolling. - Build a click-to-load button that creates the frame.
- Try to embed a site that forbids framing, and read the console error.
- Send a
postMessagefrom the frame to the parent, and checkevent.origin. - Bonus: use the builder above to produce the tag for a trusted widget, and compare it with what you wrote by hand.
Try It: Build a Safe Frameβ
Fill in the details, and the tool writes the tag and checks it.
- HTML Code
- Browser Output
<iframe
src="https://maps.example.com/embed?q=Town+Hall"
title="Map of the Town Hall"
style="width:100%;aspect-ratio:16 / 9;border:0"
loading="lazy"
sandbox="allow-scripts allow-same-origin allow-forms allow-popups"
referrerpolicy="strict-origin-when-cross-origin"
></iframe>
Quick Recapβ
Click a question to check your answer.
1. What does an iframe do?
It shows a whole other web page inside your page, with its own document, styles, and scripts.
2. Why must every iframe have a title?
Screen readers list frames by name. Without a title, the frame is announced only as "frame", and users cannot tell what it holds.
3. What does an empty sandbox attribute do?
It locks the framed page down: no scripts, forms, pop-ups, storage, or access to your page. You then add back only the permissions it needs.
4. Why is allow-scripts plus allow-same-origin dangerous?
On a page from your own origin, the page can reach into its parent and remove its own sandbox attribute, so the sandbox protects nothing. Use both only for content from a different origin that you trust.
5. Why does a site sometimes show "refused to connect" in a frame?
The site sends X-Frame-Options or a frame-ancestors policy that forbids being framed, to prevent clickjacking. You cannot override it. Use the site's official embed, or link to it.
6. How can a page and a cross-origin frame communicate safely?
With postMessage. Always name the target origin when sending, and always check event.origin when receiving.
Key Takeawaysβ
<iframe>shows another page inside yours, with its own document, styles, and scripts.- Always give it a
title, a size oraspect-ratio, and usuallyloading="lazy". - Use
sandboxfor anything you do not control, and give back only what it needs. - Never combine
allow-scriptsandallow-same-originon content from your own origin. - Grant
allowpermissions sparingly, and limit what the other site learns withreferrerpolicy. - Some sites forbid framing. Use their official embed or a link, and protect your own pages with
frame-ancestors. - Use
postMessagewith origin checks to talk to frames. - Give important information a text version, load heavy frames on click, and plan for slow connections and blocked services.
Next, learn about an older embedding element in Embed, or return to the Embedded Content overview.