Playlist

A queue of videos, with previous and next buttons and an up-next card.

import { playlist } from "react-video-kit/playlist";

Adds 1.3 KB to a player2.6 KB on its ownSource

Adds previous and next buttons to the controls, and a card offering the next video when one ends. Press Next below, or seek close to the end and wait for the card. It counts down from 3 here, then plays the next item.

First light
0:00−0:000:00

Usage

Give the plugin items, and leave src off the player:

tsx
import { VideoPlayer } from "react-video-kit/player";import { playlist } from "react-video-kit/playlist";const plugins = [  playlist({    items: [      { src: "/first.mp4", title: "First light", poster: "/first.jpg" },      { src: "/second.mp4", title: "The long ridge" },    ],  }),];<VideoPlayer plugins={plugins} />;

An item's src takes anything the player's src does, such as a list of fallbacks.

An item's title, eyebrow, and description show above the timeline while it plays, as they would on the player. To browse the queue by season, with thumbnails and running times, add the episodes plugin.

Use start to begin on another item, and loop to wrap around at the ends:

tsx
playlist({ items, start: 2, loop: true });

countdown sets how many seconds the up-next card counts down, and defaults to 5. 0 skips the card and moves on at once. null shows the card and waits for the viewer.

tsx
playlist({ items, countdown: null });

Show the queue

Read the queue with useVideoState and move through it with useCommands, from anywhere inside the player.

tsx
"use client";import { useCommands, useVideoState } from "react-video-kit";import { playlist } from "react-video-kit/playlist";function Queue() {  const items = useVideoState(playlist, (state) => state.items);  const index = useVideoState(playlist, (state) => state.index);  const { select } = useCommands(playlist);  return (    <ol>      {items.map((item, position) => (        <li key={position}>          <button aria-current={position === index} onClick={() => select(position)}>            {item.title}          </button>        </li>      ))}    </ol>  );}

select plays straight away. select(position, false) loads the item without playing. setItems replaces the queue and loads the item at the index you give it, also without playing.

Place the parts yourself

VideoPlayer places the buttons and the card through slots. When you compose your own layout, put them wherever you like:

tsx
import * as Video from "react-video-kit";import { PlaylistNext, PlaylistPrevious, PlaylistTitle, UpNext } from "react-video-kit/playlist";<Video.Root plugins={plugins}>  <Video.Media />  <UpNext />  <Video.Controls>    <PlaylistTitle />    <PlaylistPrevious />    <Video.PlayPause />    <PlaylistNext />  </Video.Controls></Video.Root>;

Behaviour

  • The queue decides what plays. The current item replaces any src on the player when it mounts.
  • Every item plays in the same <video> element, and next, previous, and select ask it to play in the same tick. A new element wouldn't have permission to autoplay on mobile.
  • Without loop, next does nothing on the last item and previous does nothing on the first. The matching button gets aria-disabled, and no card appears when the last item ends.
  • With loop, both buttons stay enabled. next on the last item goes to the first, previous on the first goes to the last, and the last item's card offers the first.
  • With a single item, the buttons don't render.
  • Play now moves on straight away, and Cancel stays on the finished video. Seeking back or replaying clears the card too, because the video is no longer ended.
  • An item with no poster removes the poster, including one set on the player.
  • An item's title appears on the up-next card. With no title, the card leaves that line out.
  • A title, eyebrow, or description on VideoPlayer replaces the current item's, so nothing changes as the queue moves.
  • The root has data-up-next while the card is showing.
  • The persistence plugin saves a position for each item, so going back to an item resumes it.
  • The media session plugin doesn't register previous or next track actions, so the system's track buttons don't move through the queue.

API

Options

OptionTypeDescription
itemsreadonly PlaylistItem[]The videos in the queue, in play order.
start?numberThe item to start on.
loop?booleanGo back to the first item after the last.
countdown?number | nullSeconds the up-next card counts down. 0 moves on at once, null waits for the viewer.

State

Read it with useVideoState(playlist, (state) => …).

FieldTypeDescription
itemsreadonly PlaylistItem[]The videos in the queue.
indexnumberPosition of the current item in items.
loopbooleanTrue when the queue wraps around at both ends.
remainingnumber | nullSeconds until the next item, while counting down.
upNextbooleanWhether the viewer can see what's next, even with no countdown running.

Commands

Call them with useCommands(playlist).

CommandTypeDescription
select(index: number, play?: boolean) => voidMoves to the item at this index and plays it, unless play is false.
next() => voidMoves to the next item and plays it, wrapping around if loop is on.
previous() => voidMoves to the previous item and plays it, wrapping around if loop is on.
cancel() => voidStays on the finished video.
setItems(items: readonly PlaylistItem[], index?: number) => voidReplaces the queue, starting from index.
nextIndex() => number | nullThe next item's position, or null at the end.

Parts

PartDescription
PlaylistNextHidden with a single item. Disabled at the end, unless looping wraps it around.
PlaylistPreviousHidden with a single item. Disabled at the start, unless looping wraps it around.
PlaylistTitleThe current item's title, eyebrow, and description. Hidden when it has none of them.
UpNextOffers the next item when one finishes, counting down if configured to.