NEWMeet the new Video Gallery — YouTube, TikTok, Instagram Reels, Vimeo and Twitch in one widget.
Guide

HTML Video Tag: The Complete Guide with Examples

Updated September 2026 · 26 min read

Key takeaways

  • Use <video> with an MP4 (H.264/AAC) source; add WebM first if you want smaller files.

  • Add controls, a poster and preload="metadata" — autoplay only when muted and inline.

  • Reserve space with aspect-ratio and use object-fit to fit any layout.

  • Add captions with <track> for accessibility.

  • For YouTube or Vimeo use their iframe players; for many videos use a gallery.

Basic example

<video controls width="640" poster="poster.jpg">
  <source src="movie.webm" type="video/webm">
  <source src="movie.mp4" type="video/mp4">
  Your browser does not support HTML5 video.
</video>

Attributes

Attribute
What it does
controls
Shows play, volume and fullscreen controls
autoplay
Starts playback automatically (needs muted)
muted
Starts without sound
loop
Replays when it ends
playsinline
Plays inline on iPhone
poster
Image shown before playback
preload
none / metadata / auto — how much to load up front
width / height
Intrinsic size (use CSS for responsive)

controls

What it does

Shows play, volume and fullscreen controls

autoplay

What it does

Starts playback automatically (needs muted)

muted

What it does

Starts without sound

loop

What it does

Replays when it ends

playsinline

What it does

Plays inline on iPhone

poster

What it does

Image shown before playback

preload

What it does

none / metadata / auto — how much to load up front

width / height

What it does

Intrinsic size (use CSS for responsive)

Responsive video

video{width:100%;height:auto;aspect-ratio:16/9}

Captions

<video controls>
  <source src="movie.mp4" type="video/mp4">
  <track kind="captions" src="captions.vtt" srclang="en" label="English" default>
</video>

Embedding YouTube isn’t the video tag

YouTube and Vimeo videos can’t be played with <video>; use their iframe players — see our YouTube embed code generator.

Formats and browser support

Format
Support
Use it for
MP4 (H.264 + AAC)
All modern browsers
The default choice
WebM (VP9 / AV1)
All modern browsers except some older Safari versions
Smaller files — offer as an extra <source>
HLS (.m3u8)
Native in Safari; others need hls.js
Long videos and live streams

MP4 (H.264 + AAC)

Support

All modern browsers

Use it for

The default choice

WebM (VP9 / AV1)

Support

All modern browsers except some older Safari versions

Use it for

Smaller files — offer as an extra <source>

HLS (.m3u8)

Support

Native in Safari; others need hls.js

Use it for

Long videos and live streams

Control the video with JavaScript

const v = document.querySelector("video");
v.play();            // returns a Promise
v.pause();
v.currentTime = 30;  // seek to 0:30
v.muted = true;
v.addEventListener("ended", () => console.log("done"));
v.addEventListener("timeupdate", () => console.log(v.currentTime));

Handle the Promise from play() — it rejects when the browser blocks playback.

Lazy loading and performance

  • preload="none" or "metadata" keeps the initial page light.

  • A poster image shows instantly.

  • Use loading="lazy" on iframes; for <video>, set the src when it scrolls into view.

  • Reserve space with aspect-ratio to avoid layout shift.

Accessibility checklist

  • Add captions with <track kind="captions">.

  • Keep controls unless you provide your own accessible controls.

  • Never autoplay with sound.

  • Provide a transcript for important videos.

Common problems

  1. Video doesn’t play on iPhone

    Add playsinline and muted for autoplay; check the codec is H.264.

  2. Black screen before play

    Add a poster image.

  3. Wrong size on mobile

    Use width:100%; height:auto; aspect-ratio.

  4. Large file loads slowly

    Compress it or stream it from a video platform.

How the browser picks a source

The browser reads the <source> elements from top to bottom and plays the first one it supports. That’s why you list the smaller, more modern format first (WebM) and the universally supported one (MP4) second. The type attribute lets the browser skip files it can’t play without downloading them — always include it. Text placed inside <video> after the sources is shown only by browsers that don’t support video at all.

<video controls preload="metadata" poster="poster.jpg">
  <source src="clip.webm" type='video/webm; codecs="vp9, opus"'>
  <source src="clip.mp4"  type='video/mp4; codecs="avc1.42E01E, mp4a.40.2"'>
  <a href="clip.mp4">Download the video</a>
</video>

Fit video into any layout

Use CSS to control how the video fills its box. object-fit: cover fills the area and crops the edges — ideal for hero sections. object-fit: contain shows the whole frame with bars. Combine it with aspect-ratio to reserve space before the file loads and prevent layout shift.

.video-box{aspect-ratio:16/9;width:100%}
.video-box video{width:100%;height:100%;object-fit:cover}

/* vertical clips */
.video-vertical{aspect-ratio:9/16;max-width:360px}

Picture-in-picture and fullscreen

Modern browsers let you move a video into a floating window or fullscreen from JavaScript — both need a user gesture such as a click.

const v = document.querySelector("video");

pipButton.onclick = async () => {
  if (document.pictureInPictureElement) await document.exitPictureInPicture();
  else await v.requestPictureInPicture();
};

fullButton.onclick = () => v.requestFullscreen();

Add disablepictureinpicture to the element if you don’t want the browser’s own PiP button.

Track video engagement

The video element fires events you can send to your analytics tool. Track starts, completions and progress milestones — not every timeupdate, which fires several times per second.

const v = document.querySelector("video");
const sent = new Set();
const track = (name) => window.gtag && gtag("event", name, { video_title: v.dataset.title });

v.addEventListener("play", () => track("video_start"), { once: true });
v.addEventListener("ended", () => track("video_complete"));
v.addEventListener("timeupdate", () => {
  const p = Math.floor((v.currentTime / v.duration) * 4) * 25; // 25, 50, 75
  if (p && p < 100 && !sent.has(p)) { sent.add(p); track("video_progress_" + p); }
});

Video tag, iframe or gallery?

The video tag is the right tool for your own short files: product loops, hero backgrounds, short explainers you host on a CDN. For videos on YouTube or Vimeo, use their iframe players — they handle streaming, quality switching and captions. When you need many videos, feeds that update themselves, or a mix of platforms, a gallery widget saves you from building playlists, popups and layouts by hand.

Frequently asked questions

Official documentation

Related

Video Embed Code Generator

Get a free embed code for a video hosted anywhere: an MP4 file, Google Drive, Dropbox, Loom or Wistia.Video Embed Code Generator

Video.js vs react-player vs hls.js vs Plyr

Video.js is a full, plugin-rich player; Plyr is a light, good-looking player; react-player wraps many sources for React; hls.js adds HLS streaming to browsers that lack it.Video.js vs react-player vs hls.js vs Plyr