Images, Media, and Embeds
Responsive images, audio/video, and iframe embeds
HTML provides native elements for embedding video, audio, images, and external content. Using the right element gives you built-in browser controls, accessibility, and better performance than plugin-based approaches.
The <video> Element
Provide multiple source formats for cross-browser support. Browsers pick the first format they understand.
<video
width="800"
height="450"
controls
muted
preload="metadata"
poster="thumbnail.jpg"
>
<source src="video.webm" type="video/webm">
<source src="video.mp4" type="video/mp4">
<track kind="subtitles" src="subs-en.vtt" srclang="en" label="English" default>
<p>Your browser does not support HTML video. <a href="video.mp4">Download it</a>.</p>
</video>
controls— show native play/pause/volume UIautoplay muted— browsers only allow autoplay when mutedpreload="none|metadata|auto"— controls how eagerly the browser fetchesposter— image displayed before the video loads<track>— captions/subtitles in WebVTT format (required for accessibility)
The <audio> Element
<audio controls preload="none">
<source src="podcast.opus" type="audio/ogg; codecs=opus">
<source src="podcast.mp3" type="audio/mpeg">
<track kind="captions" src="captions.vtt" srclang="en">
</audio>
Responsive Images with <picture>
The <picture> element lets you serve different images based on screen size or format support. The browser picks the first matching <source>; <img> is the fallback.
<picture>
<!-- Modern format for supporting browsers -->
<source
srcset="hero-800.avif 800w, hero-1600.avif 1600w"
sizes="(max-width: 600px) 100vw, 50vw"
type="image/avif"
>
<source
srcset="hero-800.webp 800w, hero-1600.webp 1600w"
sizes="(max-width: 600px) 100vw, 50vw"
type="image/webp"
>
<!-- Fallback -->
<img src="hero-800.jpg" alt="Mountain landscape at sunrise" width="800" height="450" loading="lazy">
</picture>
Embedding External Content with <iframe>
<!-- YouTube embed -->
<iframe
width="560"
height="315"
src="https://www.youtube.com/embed/VIDEO_ID"
title="Video title"
loading="lazy"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope"
allowfullscreen
></iframe>
<!-- Google Map -->
<iframe
src="https://maps.google.com/maps?q=..."
loading="lazy"
title="Office location map"
></iframe>
Performance and Lazy Loading
<!-- Native lazy loading for images and iframes -->
<img src="image.jpg" alt="..." loading="lazy" decoding="async">
<iframe src="..." loading="lazy" title="..."></iframe>
<!-- Facade pattern for heavy embeds (load on interaction) -->
<div id="youtube-facade" style="cursor:pointer" onclick="loadYouTube(this)"
data-id="VIDEO_ID"
style="background: url(thumbnail.jpg) center/cover"
aria-label="Play video"
role="button" tabindex="0"
></div>
Best Practices
- Always include
alton<img>andtitleon<iframe> - Add
<track kind="captions">to all video and audio — required for WCAG 2.1 AA - Use
loading="lazy"on below-the-fold images and iframes - Prefer
<picture>with AVIF/WebP sources over a single JPEG - Set explicit
widthandheighton images to prevent layout shift (CLS)