An Entire Page Inside Your Page
An iframe — short for inline frame — embeds a complete, separate web page inside a rectangle on your own page. Not a picture of one, and not a copy of its content: a real page, loaded from its own address, with its own HTML, CSS and JavaScript, running independently of yours.
That is a bigger thing than it sounds. Everything you have learned so far describes one document. An iframe puts a second document inside the first, and the two are genuinely separate: your CSS does not style the framed page, your JavaScript cannot read it if it comes from a different site, and its scripts cannot read yours. The browser enforces that wall — it is called the same-origin policy — and it is the reason embedding a third-party widget is possible at all.
<iframe> needs both an opening and a closing tag, even though you put nothing between them. Any text you do write between the tags is ignored by every current browser; it is a leftover from the days when some browsers had no iframe support, and it is not a fallback you can rely on today.
<!-- The minimum: a source and a title -->
<iframe src="https://example.com/widget"
title="Live seat availability"
width="600"
height="400">
</iframe>
<!-- Obsolete attributes you will see in old code -->
<iframe src="..." frameborder="0" scrolling="no" marginwidth="0"></iframe>
<!-- Do it in CSS instead -->
iframe {
border: 0;
} - Many sites refuse to be embedded. Banks, search engines and most login pages send a header telling browsers not to allow it, and your iframe shows a blank box or a refusal message instead. That is the site protecting its users, not a bug in your code — if a page will not embed, it does not want to be embedded.
What Iframes Are Actually Used For
In everyday work you will meet iframes in a small number of specific roles, and almost all of them involve embedding something from another company that you could not build yourself.
Video is the commonest. Serving video from your own hosting is expensive and technically demanding — you need multiple resolutions, adaptive streaming and a player that works everywhere. Embedding a YouTube or Vimeo player with an iframe gives you all of that for free. Maps are the same story: an embedded map from a mapping service is a full interactive application you did not have to write.
Payment forms are a subtler case that is worth understanding. Payment providers often supply their card form as an iframe precisely because of the wall between documents. The card number is typed into their page, not yours, so it never touches your server — which removes a large part of the security burden from you. When a service gives you an iframe rather than a script, this is usually why.
What iframes should not be used for is building your own site out of pieces — a frame for the header, a frame for the menu, a frame for the content. That was a real technique in the 1990s and it is thoroughly obsolete. Each frame is a separate page, so the browser's back button, bookmarking, printing and search indexing all behave wrongly. To reuse a header across pages, use a template system, a static site generator, or a server-side include.
<!-- A YouTube embed, with the attributes that matter -->
<iframe
src="https://www.youtube.com/embed/VIDEO_ID"
title="Line-following robot: full build walkthrough"
width="560" height="315"
loading="lazy"
allow="accelerometer; encrypted-media; picture-in-picture"
allowfullscreen>
</iframe>
<!-- A map embed, taken from the provider's own share dialog -->
<iframe
src="https://www.example-maps.com/embed?place=campus"
title="Map showing the college campus entrance"
width="600" height="450"
loading="lazy"
style="border: 0;">
</iframe> - Video and audio players hosted by another service
- Interactive maps
- Payment card forms, kept deliberately outside your own page
- Third-party widgets such as booking calendars, chat boxes and comment systems
- Live previews in an online code editor — including the one on this page
- Not for page layout: use templates or server-side includes for shared headers and menus
The title Attribute Is Not Optional
Every iframe needs a title attribute describing what it contains. This is the accessibility requirement people most often miss, partly because title elsewhere in HTML is only a tooltip and easy to dismiss as decorative. On an iframe it is different.
A screen reader announces an embedded frame as a distinct region the user can move into. Without a title, that announcement is "frame" and nothing else — the user has no idea whether it holds a video worth watching, a map, an advertisement, or something they should ignore. With title="Line-following robot: full build walkthrough", they can make that decision in a second.
Write it as a description of the content, not of the mechanism. "YouTube video player" tells the listener the technology and nothing about the content. Also give each iframe on a page a different title; three frames all called "Video" are as unhelpful as none.
Keyboard users have a related problem worth knowing about. Tabbing into an iframe moves focus into the embedded page, and everything focusable inside it is now in the tab order. A frame with a long, complicated page inside can therefore take many presses to get past, which is one more reason not to embed more than you need.
<!-- Useless: describes the technology, not the content -->
<iframe src="https://www.youtube.com/embed/VIDEO_ID"
title="YouTube video player"></iframe>
<!-- No title at all: announced only as "frame" -->
<iframe src="https://www.youtube.com/embed/VIDEO_ID"></iframe>
<!-- Useful: says what is inside -->
<iframe src="https://www.youtube.com/embed/VIDEO_ID"
title="Line-following robot: full build walkthrough"></iframe>
<iframe src="https://www.example-maps.com/embed?place=campus"
title="Map showing the college campus entrance"></iframe> - Automated accessibility checkers flag a missing iframe title immediately, which makes it one of the easiest problems to find and fix. Free browser extensions will scan a page in a few seconds and list every one.
Security and the sandbox Attribute
Embedding an iframe means running someone else's code inside your page. The same-origin policy stops them reading your page's content, which is a strong protection, but it does not stop everything. The embedded page can run its own scripts, set its own cookies, see the address of the page it is embedded in, and change what it serves at any time without telling you. Embed only from sources you would trust with your visitors.
The sandbox attribute lets you restrict what the framed page may do. Written on its own, with no value, it applies the maximum restrictions: no scripts, no forms, no popups, no navigation of the parent page. You then add back only the permissions the content genuinely needs, one keyword at a time.
There is a specific catch that is worth knowing rather than discovering. Granting both allow-scripts and allow-same-origin to content from your own origin lets that content remove its own sandbox attribute, which defeats the point entirely. If you are sandboxing untrusted content, do not hand out both.
Two more attributes are useful in the same area. referrerpolicy controls how much of your page's address is sent to the embedded site. And loading="lazy" matters more on iframes than on images, because an iframe loads an entire page with all its own images and scripts — three embedded videos can easily download more than the rest of your site put together. Lazy loading defers that until the visitor scrolls near it.
<!-- Maximum restriction: no scripts, no forms, no popups -->
<iframe src="https://example.com/embed"
title="Submitted project preview"
sandbox></iframe>
<!-- Add back only what the content needs -->
<iframe src="https://example.com/embed"
title="Submitted project preview"
sandbox="allow-scripts allow-forms"
referrerpolicy="no-referrer"
loading="lazy"></iframe> sandboxalone — the strictest setting; start here and relax itallow-scripts— lets the embedded page run JavaScriptallow-forms— lets it submit formsallow-popups— lets it open new windowsallow-same-origin— treats it as coming from its own origin; do not combine withallow-scriptsfor untrusted contentloading="lazy"— do not load the embedded page until it is nearly on screen
- Each iframe is a full page load, with its own requests, its own memory and its own scripts. On a slow mobile connection, a page with several embedded videos can take many seconds longer than the same page with thumbnail images that load the player only when clicked.
Making an Embed Responsive
The width and height attributes you copy from an embed code are fixed pixel values, so a 560-pixel-wide video overflows the screen on a phone. Setting width: 100% alone does not fix it, because the height stays fixed and the video ends up stretched or letterboxed.
The modern solution is the CSS aspect-ratio property. Give the iframe a full width and an aspect ratio of 16 / 9, and the browser works out the height itself at every screen size. Keep the width and height attributes in the HTML as well, so the browser can reserve the right amount of space before the embed loads — exactly the layout-shift argument from the images lesson.
You may still see an older technique in tutorials, using a wrapper element with padding-bottom: 56.25% and absolute positioning. It works and there is nothing wrong with it, but aspect-ratio does the same job in two lines and is supported by all current browsers.
<div class="video-embed">
<iframe
src="https://www.youtube.com/embed/VIDEO_ID"
title="Line-following robot: full build walkthrough"
width="560" height="315"
loading="lazy"
allowfullscreen>
</iframe>
</div>
.video-embed iframe {
width: 100%;
height: auto;
aspect-ratio: 16 / 9;
border: 0;
border-radius: 8px;
} 