preloader
post-thumb

Last Update: February 20, 2025 3 min read


BYauthor-thumberic

|Loading...

Video Not Autoplaying in React or Next.js? Use autoPlay, Not autoplay

Short answer: in JSX the attribute is autoPlay with a capital P. If you copy plain HTML and write autoplay, React doesn't recognise it, drops it, and your video loads but just sits on the first frame. Write it like this:

jsx
<video autoPlay muted loop playsInline>
  <source src="/videos/clip.mp4" type="video/mp4" />
</video>

muted and playsInline aren't optional extras. Browsers block autoplay with sound, and iPhones won't play a video inline without playsinline.

What happened

I made a short clip with Sora and tried to use it in a Next.js page. The video loaded, but it never started playing. This is the code I had (with a public Wikimedia test clip instead of my Sora video):

jsx
export default function Page() {
    return (
        <div>
            <h1>Video</h1>
            <video autoplay muted loop>
                {/* This is not the video I created with Sora */}
                <source src="https://upload.wikimedia.org/wikipedia/commons/0/09/Frozen_drop.webm" type="video/webm" />
                Your browser does not support the video tag.
            </video>
        </div>
    );
}

It looks right because it's valid HTML. In React it isn't, and the fix was changing autoplay to autoPlay.

Why lowercase autoplay is ignored

React uses camelCase names for DOM attributes: className, tabIndex, autoPlay, playsInline. A lowercase autoplay is treated as an unknown attribute. Writing it with no value means autoplay={true}, and React won't put a boolean true on an attribute it doesn't know. So it's left out of the HTML.

You can see this in the browser console in development:

Invalid DOM property `autoplay`. Did you mean `autoPlay`?

Here is what React 19 actually renders for each spelling. It is the same from server rendering and in the browser:

What you write in JSX
Rendered video tag
Autoplays?
autoplay
no autoplay attribute (plus a console warning)
No
autoPlay
autoplay=""
Yes
autoPlay="true"
autoplay="" (plus a console warning)
Yes
autoPlay="false"
autoplay="" (plus a console warning)
Yes, which is probably not what you meant
autoPlay=""
no autoplay attribute
No

An earlier version of this post suggested autoPlay="true", autoPlay="autoplay" or autoPlay="". The first two work but trigger a warning, and autoPlay="" doesn't work at all because React treats an empty string as false. Just write autoPlay on its own, or autoPlay={true}.

The same applies to the other video attributes. Use playsInline, not playsinline. muted, loop, controls and poster are already single words, so they're fine as they are.

The fixed component

jsx
export default function Page() {
    return (
        <div>
            <h1>Video</h1>
            <video autoPlay muted loop playsInline>
                <source src="https://upload.wikimedia.org/wikipedia/commons/0/09/Frozen_drop.webm" type="video/webm" />
                Your browser does not support the video tag.
            </video>
        </div>
    );
}

This works as a Server Component in the Next.js App Router. It doesn't need "use client" because there's no state or effect.

If it still won't autoplay

Getting the attribute name right is the usual cause. If the video still sits there, check these:

  1. Is it muted? Chrome, Safari and Firefox all block autoplay with sound unless the user has already interacted with the site. muted is the reliable way through. If you need sound, start muted and add an unmute button.

  2. Is playsInline there? On iPhone, a video without playsinline won't play inline on the page. Add playsInline for any background or hero video.

  3. Can the browser play the format? .webm isn't supported everywhere, particularly on older Apple devices. H.264 MP4 is the safe default. You can list both and the browser picks the first one it supports:

    jsx
    <video autoPlay muted loop playsInline>
        <source src="/videos/clip.webm" type="video/webm" />
        <source src="/videos/clip.mp4" type="video/mp4" />
    </video>
    

    If you need to convert or shrink a clip, my FFmpeg on Ubuntu commands post has the commands.

  4. Is the device in a power-saving mode? Some phones in low power or data-saver modes don't autoplay video at all. Your code can't override that, so set a poster image so the page still looks right.

  5. Is the file path right? In Next.js, files in public/ are served from the site root. public/videos/clip.mp4 is /videos/clip.mp4, not /public/videos/clip.mp4. Check the Network tab for a 404.

Fallback: start it with play() and catch the error

If you want to know when autoplay was blocked, or you swap the src after the first render, start the video from an effect. play() returns a promise that rejects (usually with NotAllowedError) when the browser refuses. In the App Router this has to be a Client Component:

jsx
"use client";

import { useEffect, useRef } from "react";

export default function BackgroundVideo({ src, poster }) {
    const ref = useRef(null);

    useEffect(() => {
        const video = ref.current;
        if (!video) return;
        video.muted = true; // set the property as well, before calling play()
        video.play().catch((err) => {
            // Autoplay was blocked. The poster stays visible.
            console.warn("Autoplay blocked:", err.name);
        });
    }, [src]);

    return (
        <video ref={ref} src={src} poster={poster} autoPlay muted loop playsInline />
    );
}

Setting video.muted = true in code is belt and braces. React sets muted as a DOM property rather than an attribute when it renders in the browser, so you won't see muted when you inspect the element even though the video is muted.

The video

Here's the Sora clip that started all this:

If you're comparing AI video tools, see Exploring AI video generation.

Comments (0)

Leave a Comment
Your email won't be published. We'll only use it to notify you of replies to your comment.
Loading comments...
Previous Article
post-thumb

Feb 20, 2025

Kling vs Runway vs Pika vs Luma vs Sora vs Hailuo: Same Three Prompts Tested

I gave the same three text prompts (a neon concept car, a hotel ad and a zombie at a railway crossing) to six AI video generators in February 2025. Here are all 17 clips, which tool won each round, where each one ignored the prompt, and what the plans cost at the time.

Next Article
post-thumb

Feb 08, 2025

Blending Two Famous Faces in Stable Diffusion: Prompt Editing, Weights and My Real Settings

My attempts at a Lincoln and Henry Cavill face blend in AUTOMATIC1111, with the exact prompts, seeds and models read from each image, why my original '| :0.5' scheduling syntax was wrong, and the correct [from:to:when] form.

agico

We transform visions into reality. We specializes in crafting digital experiences that captivate, engage, and innovate. With a fusion of creativity and expertise, we bring your ideas to life, one pixel at a time. Let's build the future together.

Copyright ©  2026  TYO Lab · v0.0.38