CLI tool
muxinc/next-video avatar
muxinc/next-video

next-video: a video element for Next.js that pushes the hard parts to a video API

The easiest way to add video in your Nextjs app.

1,224 stars71 forksTypeScriptMIT

At a glance

What is it?
next-video is a React component from Mux Lab that turns an imported video file into an optimized, CDN-delivered stream with posters, hover thumbnails, optional analytics and captions, by syncing your source files to a video platform during development. The convenience is real, and so is the dependency: remote storage and optimization default to Mux.
Who is it for?
next-video fits a Next.js application that needs to ship video, wants automatic encoding and delivery instead of a checked-in MP4, and is willing to put its media through a hosted video platform. It does not fit a project that cannot send its video to a third party, and it does not fit a team that only wants a styled player over files it hosts itself.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 22 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The import is the API, not a URL

next-video is a React component for adding video to a Next.js application, and it extends both the video element and your Next app with features for automatic video optimization.

The usage is three lines and the third one is the surprise:

tsx
import Video from 'next-video';
import getStarted from '/videos/get-started.mp4';

export default function Page() {
  return <Video src={getStarted} />;
}

The video is imported as a module, the way you would import an image. The bundler resolves the import, and the sync step that runs alongside the dev server has already uploaded and processed the file, so the component renders a player pointing at an optimized, CDN-served asset rather than at the original file in your repository.

That is the whole idea, and it is a good one for the case it targets. A video file in a git repository is a liability: it bloats the clone, it is never re-encoded, and every browser gets a format it may not support. The feature list names the consequences directly, with smart storage to keep large files outside the git repo, auto optimized delivery via CDN, wider compatibility for videos that aren't supported natively by all browsers, and zero-config placeholder images and timeline hover thumbnails.

The package is MIT licensed, written in TypeScript, maintained by Mux Lab, and the documentation site is next-video.dev. The current version is 2.8.2, and the last push to the repository was on 2026-10-01.

One init command edits five files for you

The setup is designed to be a single command, and the list of what it touches is worth reading before you run it.

In the root of your Next.js project, npx -y next-video init will, with prompting: install next-video as a dependency, create a /videos directory and update .gitignore, update your next.config.js file, add types for your video file imports if you are using TypeScript, add next-video sync -w to your dev script in package.json, set up Mux credentials in .env.local for remote storage, and add a sample video and demo page to test your setup.

That is a script editing your build configuration, your dev script, your git ignore file and your environment file, which is convenient and is also the point at which you should read what it did. The .gitignore change is the one with a rationale behind it: videos, particularly any of reasonable size, shouldn't be stored or tracked by git. The alternative, if you would rather store the original files, is to remove the added gitignore lines and install git-lfs.

The manual route is documented in full, and the steps are the same ones the script performs. You install the package with npm install next-video, yarn add next-video or pnpm add next-video depending on your package manager, create the videos directory with mkdir videos, and add a block to .gitignore that ignores the media while keeping the metadata:

code
videos/*
!videos/*.json
!videos/*.js
!videos/*.ts
public/_next-video

That pattern is the interesting part. The video files are ignored, the JSON, JS and TS sidecars are not, and the generated output directory public/_next-video is ignored too. So the repository keeps the metadata that describes the video and the code that renders it, and nothing else.

Mux is the default remote store, and that is the trade

The remote storage story is where this package differs from a plain video tag, and it is worth being explicit about it.

The README cites Vercel's recommendation to use a dedicated content platform for video because video files are large and can lead to excessive bandwidth usage. By default, next-video uses Mux, described as a video API for developers built by the creators of Video.js.

The setup is two environment variables, MUX_TOKEN_ID and MUX_TOKEN_SECRET in .env.local, and .env.local should be in your .gitignore. The token needs Mux Video permissions.

So the default path is: your source file is uploaded to a hosted video platform, processed there, and served from that platform's CDN. The advantages are the ones the feature list claims, automatic optimization, delivery via CDN, posters and hover thumbnails, optional analytics, and AI-generated captions and transcripts through Mux's own tooling. The cost is that your media now lives with a third party, and the pipeline is no longer something you run on your own infrastructure.

That is a reasonable default for the target case, where the alternative is shipping large files from a Next.js deployment, and an unreasonable default for a project with data residency requirements, a media library that already exists elsewhere, or a policy against uploading assets to a third party.

There is a local path too. The sync command is what performs the upload and processing, and the analytics entry in the feature list is marked optional. A team that wants the component and the player without the remote pipeline has to work out that configuration, and the README points at the documentation site rather than spelling it out.

Turbopack and webpack disagree about import paths

One configuration detail in the manual setup is worth knowing about before you hit it, because the error it causes is not self-explanatory.

If you are using Turbopack, which the README notes is the default dev server in recent versions of Next.js, you need to add a path alias so that /videos imports resolve correctly. The tsconfig.json change is:

json
{
  "compilerOptions": {
    "paths": {
      "@/*": ["./src/*"],
      "@videos/*": ["./videos/*"]
    }
  }
}

And then you import with the alias instead:

tsx
import getStarted from '@videos/get-started.mp4';

If you are using webpack, that is, running next dev --webpack, /videos imports work without any additional path configuration.

The reason for the difference is the import itself. The bundler has to resolve a path that looks absolute into a file in your project, and the two bundlers disagree about how to do that for a root-relative path. Turbopack needs to be told where the videos live; webpack infers it.

The rest of the manual setup follows the same logic of keeping the type checker happy. A video.d.ts file in the project root with a reference to next-video/video-types/global, added to the include array in tsconfig.json, is what stops the imports from complaining about missing types. And the dev script becomes:

json
"scripts": {
  "dev": "next dev & npx next-video sync -w"
}

That last line is the mechanism of the whole tool. The sync command runs in watch mode alongside the dev server, so an edited or newly added video is uploaded and processed while you work.

The exports map is where the extension points live

The package's public surface is more than the default component, and the exports map in package.json is a compact description of what you can do with it.

The root export is the Video component, and the main field points at the same file, dist/components/video.js. There is a player export at./player, which resolves to the default player component, and a./process export with separate import and require conditions, which is the next-video configuration wrapper.

The rest of the map is a list of the other entry points. The./webpack/* export exists so that a Next.js webpack configuration can pull in the loader or plugin that handles video imports, and the./process export is the same idea for the general configuration path, since the manual setup wraps the config with withNextVideo in both the CommonJS and the ES module form. The./request-handler export exists for serving video through a route handler rather than through the static pipeline, and there are./background-video and./background-player exports for a background video use case, plus./video-types/* for the TypeScript declarations and a dist/* catch-all.

The build scripts in package.json show how the two module systems are produced. The types are compiled twice, with tsc --outDir dist and tsc --outDir dist/cjs, and the exports map points the require conditions at the cjs directory and the import conditions at the ESM build. The files array publishes only dist and video-types, so the source, tests and examples stay out of the tarball.

The practical reading for an adopter is that the video element is the easy part, and the interesting part is that the library exposes its own config wrapper, its bundler plugin and a request handler. That is what makes it possible to use the same component in a build that deploys to a CDN and in one that serves video from a route handler.

MIT licensed, but the interesting part is hosted

All files in the package are MIT licensed, with the LICENSE file at the root of the repository, and the package metadata records the same. There is no copyleft obligation and no restriction on using it in a commercial application.

What the licence does not cover is the service on the other side of the pipeline. The repository is by Mux Lab, the author in the package metadata is Mux Lab, and the default remote storage and optimization backend is Mux, which is a commercial service with its own pricing and its own terms. The code being permissively licensed does not make the media pipeline free, and a team that reads MIT and concludes the whole arrangement is costless will be disappointed.

The release history suggests an active project with a fairly wide compatibility surface. Versions 2.8.0, 2.8.1 and 2.8.2 were published on 2026-05-19, 2026-06-08 and 2026-10-01, and the last push to the repository was on 2026-10-01. The tree is small and conventional, with src, tests, examples including a default-provider example, and a video-types directory for the TypeScript declarations.

One documentation gap to plan for. The README describes the automatic and manual setup in detail, and the feature list is thorough, but it does not document what happens to a video file after the sync command runs, beyond the fact that it is uploaded and processed. Anyone with a data residency requirement, a media retention policy, or a need to know the cost per video should read the documentation site before uploading anything.

Editorial conclusion

next-video fits a Next.js application that needs to ship video, wants automatic encoding and delivery instead of a checked-in MP4, and is willing to put its media through a hosted video platform. It does not fit a project that cannot send its video to a third party, and it does not fit a team that only wants a styled player over files it hosts itself. Before you adopt it, read what the sync command does with your source files and where it puts them, and confirm that the two Mux environment variables are the only credentials your build needs to hold.

Frequently asked questions

How do I add video to a Next.js app with next-video?

Run npx -y next-video init in the root of your project, which installs the package, creates a /videos directory, updates next.config.js and .gitignore, adds the TypeScript types, adds the sync command to your dev script, sets up Mux credentials and adds a demo page. Then import a file from /videos and render it with the Video component.

Does next-video require a Mux account?

For the default remote storage and optimization path, yes. The setup adds MUX_TOKEN_ID and MUX_TOKEN_SECRET to .env.local, with a token that has Mux Video permissions. The README says the sync command uploads and processes the file, so the media is handled by that hosted video platform unless you configure it differently.

Why does my /videos import fail under Turbopack?

Turbopack needs to be told where the videos live, so you add a path alias for @videos/* pointing at ./videos/* in compilerOptions.paths in tsconfig.json and import with that prefix. If you are using webpack, that is, running next dev --webpack, the /videos imports work without any path configuration.

How do I keep video files out of git?

The init command adds lines to .gitignore that ignore the video files while keeping their JSON, JS and TS metadata, and the generated public/_next-video directory. If you would rather store the original files in the repository, remove those lines and install git-lfs instead.

What features does next-video add over a plain video element?

Automatic optimization and CDN delivery, zero-config placeholder images and timeline hover thumbnails, customizable player controls, compatibility with videos not supported natively by all browsers, optional analytics, and AI-generated captions and transcripts through Mux's documentation.

Official sources

  1. License: MIT
  2. muxinc/next-video on GitHub
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/muxinc-next-video.svg)](https://hysenlabs.com/projects/muxinc-next-video)

Was this useful?