Set up a Video.js player that survives source changes and page navigation

Initialize Video.js once for a mounted video element, change its media with the player's src() method, and call dispose() before removing that player from the page. This matters in a training portal where a reader moves between lessons: removing the visible video alone can leave Video.js state associated with an element that no longer exists.

Video.js documents both initialization and player reuse and disposal. Load the JavaScript and stylesheet from the same pinned, tested Video.js distribution before using the examples here. The snippets assume those assets are already available; they do not include a CDN download or a working media file.

Give one element one initialization path

Use a stable ID for the current lesson player:

<video id="lesson-player" class="video-js" controls preload="metadata">
  <p>Your browser does not support this video element.</p>
</video>

Then initialize it explicitly after the element exists:

let player = videojs('lesson-player', {
  controls: true,
  preload: 'metadata',
  fluid: true
});

player.ready(function () {
  this.src({ src: '/media/safety-introduction.mp4', type: 'video/mp4' });
});

For this explicit setup, leave off the data-setup attribute used by Video.js's automatic setup route. Mixing two setup paths makes it harder to identify which configuration initialized the player.

The example path is a placeholder for media you control. Confirm that the real URL returns a playable file with the correct response type. A visible control bar establishes that the UI initialized; successful playback requires a separate media check.

Change the source on the existing instance

A second lesson in the same mounted player can reuse that instance:

function chooseLesson(source) {
  if (!player) throw new Error('Lesson player is not mounted');
  player.pause();
  player.src(source);
}

chooseLesson({
  src: '/media/emergency-exits.mp4',
  type: 'video/mp4'
});

Call this from the application's existing lesson-selection handler. Let the reader start playback with the controls. If your application also calls play(), handle its returned promise and provide a usable play control when the browser refuses playback.

For an original acceptance case, start the fictional safety introduction, select “Emergency exits,” and inspect the active source and the displayed content. The expected result is the exit lesson in one player, with no continuing audio from the introduction. Check again after rapidly choosing two lessons; the final selected lesson should match the page's title and description.

HTML video troubleshooting helps separate a bad media path from an initialization problem. An HLS manifest adds playlist, segment, and browser-support requirements beyond this MP4 lifecycle example.

Dispose before removing the player's route

Wire cleanup into the application code that unmounts this lesson view:

function unmountLessonPlayer() {
  if (player) {
    player.dispose();
    player = null;
  }
}

Video.js disposal removes its player element and cleans up its internal state. When the lesson route mounts again, create fresh video markup and initialize a new instance on it. Do not try to reinitialize the disposed element or retain a reference to the old player for later source changes.

A useful navigation check has a specific sequence: open the lesson, start playback, leave the route, return, and start the lesson again. Check that the second visit creates one usable player, the previous audio has stopped, and the console contains no errors tied to a removed video. Repeat with the actual route framework's cleanup mechanism rather than assuming that hiding a container performs unmount cleanup.

The final handoff should include the pinned Video.js version, actual media URL, browser, and observed navigation result. That gives the next developer enough information to reproduce a lifecycle problem without confusing it with a missing file or a rejected autoplay request.

Keep reading