The iframe Element β Core Attributes
At its simplest, an iframe requires src, width, height, and title. The title attribute is required for accessibility β screen readers announce it so users know what the embedded content is.
<!-- Basic iframe -->
<iframe
src="https://example.com"
width="800"
height="500"
title="Example website">
<!-- Fallback content if iframes are disabled -->
Your browser does not support iframes.
<a href="https://example.com">Visit Example.com</a>
</iframe>
<!-- Responsive iframe using padding-bottom trick in a wrapper -->
<div style="position:relative; padding-bottom:56.25%; height:0; overflow:hidden;">
<iframe
src="https://example.com"
title="Example website"
style="position:absolute; top:0; left:0; width:100%; height:100%;"
loading="lazy">
</iframe>
</div>
title attribute is a WCAG 2.1 Level A failure. Screen readers will announce something unhelpful like "frame" with no context. Describe the content: title="Product demo video".
Embedding YouTube Videos
YouTube's shareable URL (youtube.com/watch?v=ID) cannot be embedded directly. Use the /embed/VIDEO_ID format instead. You can find the embed code from YouTube's Share β Embed menu.
<!-- Correct: use the /embed/ URL -->
<iframe
width="560"
height="315"
src="https://www.youtube.com/embed/dQw4w9WgXcQ"
title="Introduction to HTML β ylearner tutorial video"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
allowfullscreen
loading="lazy">
</iframe>
<!-- Wrong: watch URL does not work in iframes -->
<!-- <iframe src="https://www.youtube.com/watch?v=dQw4w9WgXcQ"></iframe> -->
<!-- Useful YouTube embed parameters -->
<!-- ?start=30 β start at 30 seconds -->
<!-- ?end=120 β stop at 120 seconds -->
<!-- ?autoplay=1 β autoplay (requires muted) -->
<!-- ?mute=1 β mute audio -->
<!-- ?controls=0 β hide player controls -->
<!-- ?rel=0 β don't show related videos from other channels -->
<!-- ?cc_load_policy=1 β force captions on -->
<!-- Combine with & separator: ?start=30&rel=0&cc_load_policy=1 -->
<iframe
src="https://www.youtube.com/embed/dQw4w9WgXcQ?start=30&rel=0"
title="HTML tutorial starting at 0:30"
width="560" height="315" allowfullscreen loading="lazy">
</iframe>
Embedding Google Maps
Use the Google Maps Embed API or copy the embed code from Google Maps (Share β Embed a map). You need a Maps Embed API key for custom maps; the simple "Share" embed does not require one.
<!-- Google Maps embed (from Maps β Share β Embed a map) -->
<iframe
src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d2483.3456!2d-0.1276!3d51.5074!..."
width="600"
height="450"
title="London, UK map"
style="border:0;"
allowfullscreen
loading="lazy"
referrerpolicy="no-referrer-when-downgrade">
</iframe>
<!-- Google Maps Embed API (requires API key) -->
<iframe
src="https://www.google.com/maps/embed/v1/place?key=YOUR_API_KEY&q=London,UK"
width="600"
height="450"
title="London, UK map"
allowfullscreen
loading="lazy">
</iframe>
Embedding Other HTML Documents
<!-- Embed another page from your own site -->
<iframe
src="/components/widget.html"
title="Interactive widget"
width="400"
height="300">
</iframe>
<!-- Embed a PDF (browser PDF viewer) -->
<iframe
src="/docs/report.pdf"
title="Annual Report 2026"
width="800"
height="600">
</iframe>
The sandbox Attribute β Restricting Iframe Capabilities
Adding sandbox to an iframe applies a strict security policy. A bare sandbox with no value enables all restrictions β scripts cannot run, forms cannot submit, the page cannot navigate the parent, and so on. You then selectively re-enable only what you need.
<!-- Maximum restriction: bare sandbox disables everything -->
<iframe src="untrusted.html" sandbox title="Sandboxed content"></iframe>
<!-- Allow JavaScript but nothing else -->
<iframe src="widget.html" sandbox="allow-scripts" title="Widget"></iframe>
<!-- Allow scripts and forms (e.g. a third-party survey embed) -->
<iframe src="survey.html" sandbox="allow-scripts allow-forms" title="Survey"></iframe>
<!-- Allow same-origin content to act as if not sandboxed -->
<iframe src="/internal.html" sandbox="allow-scripts allow-same-origin" title="Internal"></iframe>
| Token | What it re-enables |
|---|---|
allow-scripts | JavaScript execution |
allow-forms | Form submission |
allow-same-origin | Treats content as same-origin (access cookies, storage) |
allow-popups | Opening new windows/tabs with window.open() |
allow-top-navigation | Navigating the parent page's URL |
allow-downloads | Downloading files |
allow-modals | alert(), confirm(), prompt() dialogs |
allow-scripts and allow-same-origin for untrusted content β together they allow the iframe to remove its own sandbox restrictions.
The allow Attribute β Feature Permissions
The allow attribute controls which browser APIs the embedded page can access β camera, microphone, fullscreen, payment, and more. It follows the Permissions Policy (formerly Feature Policy) syntax.
<!-- YouTube requires accelerometer, autoplay, clipboard-write, etc. -->
<iframe
src="https://www.youtube.com/embed/VIDEO_ID"
title="Tutorial video"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowfullscreen>
</iframe>
<!-- Payment request API -->
<iframe src="checkout.html" title="Checkout" allow="payment"></iframe>
<!-- Camera access for a video-call widget -->
<iframe src="videocall.html" title="Video call" allow="camera; microphone"></iframe>
<!-- Geolocation for a map widget -->
<iframe src="nearby.html" title="Nearby stores" allow="geolocation"></iframe>
Security Risks β Clickjacking and X-Frame-Options
Clickjacking is an attack where a malicious page embeds your site in a transparent iframe and overlays it with invisible buttons. When users think they are clicking on the attacker's UI, they are actually clicking on your site's buttons β confirming purchases, changing settings, or performing account actions.
Defence against clickjacking is controlled server-side, not in HTML:
- X-Frame-Options HTTP header β older, widely supported.
DENYprevents all embedding;SAMEORIGINallows only same-origin embedding. - Content-Security-Policy: frame-ancestors HTTP header β newer, more flexible. Supersedes X-Frame-Options.
<!-- These are HTTP response headers, not HTML tags.
Set them in your web server config or server-side code. -->
<!-- Nginx config example -->
<!-- add_header X-Frame-Options "SAMEORIGIN"; -->
<!-- add_header Content-Security-Policy "frame-ancestors 'self' https://trusted.com"; -->
<!-- Apache config example -->
<!-- Header always set X-Frame-Options "DENY" -->
<!-- For your own page to check if it's been framed (JavaScript defence): -->
<script>
if (window.top !== window.self) {
// This page is inside an iframe
window.top.location = window.self.location; // break out of frame
}
</script>
When Not to Use Iframes
Iframes have real drawbacks that make them the wrong choice in certain situations:
- Performance: Each iframe creates a completely separate browsing context β its own HTML parser, JavaScript engine, CSS engine. Loading several iframes on one page is expensive.
- SEO: Search engines do not reliably index the content inside iframes. Do not use iframes for content you want indexed.
- Accessibility: Some screen reader and keyboard navigation flows break when they encounter iframes, especially if keyboard focus traps are not handled. Always test with screen readers.
- Cross-origin limitations: JavaScript in your page cannot read or write to the content of a cross-origin iframe (same-origin policy).
- Mobile layout: Fixed-size iframes are difficult to make responsive and can cause horizontal scrolling.
π Summary
- Every
<iframe>must have a descriptivetitleattribute for screen reader accessibility. - To embed YouTube, use the
/embed/VIDEO_IDURL β the watch URL does not work in iframes. - Add
loading="lazy"to defer loading off-screen iframes and improve page load performance. - Use
sandboxto restrict untrusted embedded content β start with the strictest policy and only add tokens you need. - Never combine
allow-scriptsandallow-same-originon untrusted content. - The
allowattribute controls access to browser APIs (camera, microphone, fullscreen, payment, geolocation). - Protect your own pages from clickjacking with the
X-Frame-OptionsorContent-Security-Policy: frame-ancestorsHTTP headers. - Avoid iframes for SEO-critical content, performance-sensitive pages, and anywhere mobile layout is a concern.
FAQ
Why does my YouTube embed show "Video unavailable"?
The most common reason is that you used the watch URL (youtube.com/watch?v=ID) instead of the embed URL (youtube.com/embed/ID). Other reasons include: the video owner has disabled embedding for their video (set in YouTube Studio settings), the video is private or age-restricted, or the video is geo-blocked in the viewer's country. Check the video's YouTube Studio "Embed" settings if you control the video.
Can I communicate between an iframe and the parent page?
For same-origin iframes you can access the iframe's content directly via iframe.contentDocument or iframe.contentWindow. For cross-origin iframes, direct access is blocked by the Same-Origin Policy. Use the window.postMessage() API instead β both sides listen for the message event and validate the event.origin before trusting the data.
How do I make an iframe responsive?
The classic approach is a wrapper div with position: relative; padding-bottom: 56.25%; height: 0; (56.25% = 9/16 for a 16:9 ratio) and the iframe set to position: absolute; top: 0; left: 0; width: 100%; height: 100%;. Modern browsers also support the aspect-ratio CSS property: set width: 100%; aspect-ratio: 16/9; directly on the iframe β cleaner and requires no wrapper.