Skip to content

NotAllowedError on autoplay when scrolling through list of Video.js components #9144

@juniorforlife

Description

@juniorforlife

Description

I am building a vertical short-video feed (TikTok-style) using Svelte 5 and Video.js.

The app follows this logic:

  1. All videos start muted.
  2. The user interacts with the first video by clicking an "Unmute" button.
  3. This sets a global state (isMuted = false).
  4. Subsequent videos in the feed use this state to initialize and call .play() unmuted.

This works perfectly on Desktop. However, on Mobile Browsers (eg: iOS Safari), the unmuted autoplay works for approximately 5–10 videos, and then fails. The video stops playing with the following error: Unhandled Promise Rejection: NotAllowedError: The request is not allowed by the user agent or the platform in the current context.

My simplified code

<script lang="ts">
	import { VolumeX, Play } from "@lucide/svelte";
	import videojs from "video.js";
	import "video.js/dist/video-js.css";
	import { playerState } from "$lib/state/player.svelte";

	let {
		video,
		isActive = false,
		index,
	}: {
		video
		isActive?: boolean;
		index?: number;
	} = $props();

	let playerContainer: HTMLDivElement;
	let player: any = $state(null);
	let playing = $state(isActive);

	function onPlaybackToggle() {
		if (playing) {
			player?.pause();
		} else {
			player?.play();
		}
		playing = !playing;
	}

	$effect(() => {
		if (!document.getElementById(video.id)) {
				const videoElement = document.createElement("video-js");
				videoElement.style.width = "100%";
				videoElement.style.height = "100%";
				videoElement.style.objectFit = "cover";
				videoElement.id = video.id;

				playerContainer.appendChild(videoElement);

				player = videojs(videoElement, {
					muted: true,
					controls: false,
					loop: true,
					playsinline: true,
					preload: "none",
					sources: [{ src: video.hls_url }]
				});
			}
	});

	$effect(() => {
		// When the user unmutes the first video, unmute all videos
		if (!playerState.mute) {
			player?.muted(false);
		}
	});


	$effect(() => {
		if (isActive) {
			playing = true;
			player?.play();
		} else {
			playing = false;
			player?.pause();
		}
	});
</script>

<div
	data-index={index}
	bind:this={playerContainer}
>
	<button class="absolute inset-0 z-10" onclick={onPlaybackToggle}>
		{#if !playing}
		<Play class="h-12 w-12 text-white" />
		{/if}
	</button>

	{#if playerState.mute}
		<button onclick={() => (playerState.mute = false)}>
			Unmute
		</button>
	{/if}
</div>

Reduced test case

https://www.memoreels.app

Steps to reproduce

  1. Open the website on mobile Safari/Chrome
  2. Unmute the first video
  3. Swipe for a while
  4. The videos will stop playing and the error appears in Console tab

Errors

Unhandled Promise Rejection: NotAllowedError: The request is not allowed by the user agent or the platform in the current context, possibly because the user denied permission

What version of Video.js are you using?

8.12.0

Video.js plugins used.

No response

What browser(s) including version(s) does this occur with?

iOS Safari, Android Chrome

What OS(es) and version(s) does this occur with?

iOS, Android

Metadata

Metadata

Assignees

No one assigned

    Labels

    browser issueA bug or limitation of the browser

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions