next-video Documentation

repository·main·Indexed 23 days ago

https://github.com/muxinc/next-video

A React component for Next.js applications that provides automatic video optimization, smart storage via Mux, and enhanced player features such as posters, previews, and AI-powered captions. It includes tools for syncing local videos, adopting existing Mux assets, and support for multiple hosting providers including Vercel Blob, Backblaze, Amazon S3, and Cloudflare R2.

Tokens
9.1K
Snippets
18
Records
42
Agent score
78%

What's inside next-video

  1. Quickstart: Add video to your Next.js application

    main

    To use next-video, import the Video component and pass a video file import as the src prop. This component handles automatic optimization and delivery via CDN.

    import Video from 'next-video';
    import getStarted from '/videos/get-started.mp4';
    
    export default function Page() {
      return <Video src={getStarted} />;
    }
    import Video from 'next-video';
    import getStarted from '/videos/get-started.mp4';
    
    export default function Page() {
      return <Video src={getStarted} />;
    }
  2. Use local videos with next-video

    main

    To use videos stored locally, add them to the /videos directory and run the sync command to upload and optimize them. This process creates .json files in the /videos folder that map local files to remote assets; these JSON files must be committed to git.

    Syncing videos

    Run the following command to sync your local videos:

    npx next-video sync

    To automatically sync videos as you add them while your development server is running, add the -w flag to your dev script in package.json:

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

    Importing local videos

    Once synced, import the video file directly into your component:

    import Video from 'next-video';
    import awesomeVideo from '/videos/awesome-video.mp4';
    
    export default function Page() {
      return <Video src={awesomeVideo} />;
    }

    Note for Turbopack users: Turbopack does not resolve /videos imports by default. You must add a path alias to your tsconfig.json:

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

    Then use @videos/awesome-video.mp4 in your imports.

    npx next-video sync
  3. Configure Mux for remote storage and optimization

    main

    By default, next-video uses Mux for remote storage and video optimization. To enable this, you must provide your Mux credentials in your .env.local file.

    1. Sign up for Mux.
    2. Create an access token with Mux Video permissions.
    3. Add the following environment variables to your .env.local:
    # .env.local
    MUX_TOKEN_ID=[YOUR_TOKEN_ID]
    MUX_TOKEN_SECRET=[YOUR_TOKEN_SECRET]
    # .env.local
    MUX_TOKEN_ID=[YOUR_TOKEN_ID]
    MUX_TOKEN_SECRET=[YOUR_TOKEN_SECRET]
  4. Manual Setup: Install and Configure next-video

    main

    If you prefer not to use the automatic init, follow these steps to manually integrate next-video into your Next.js project.

    1. Install the package

    # NPM
    npm install next-video
    
    # Yarn
    yarn add next-video
    
    # pnpm
    pnpm add next-video

    2. Create the videos directory

    Create a /videos directory in your project root to store source files:

    mkdir videos

    Add these lines to your .gitignore to keep large video files out of git while tracking their JSON metadata:

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

    3. Update Next.js configuration

    Wrap your Next.js configuration with withNextVideo from next-video/process.

    For CommonJS (next.config.js):

    const { withNextVideo } = require('next-video/process');
    
    /** @type {import('next').NextConfig} */
    const nextConfig = {};
    
    module.exports = withNextVideo(nextConfig);

    For ES Modules (next.config.mjs):

    import { withNextVideo } from 'next-video/process';
    
    /** @type {import('next').NextConfig} */
    const nextConfig = {};
    
    export default withNextVideo(nextConfig);

    4. Add video import types (TypeScript only)

    Create a video.d.ts file in your project root:

    /// <reference types="next-video/video-types/global" />

    Add this file to the include array in your tsconfig.json:

    {
      "include": ["video.d.ts", "next-env.d.ts", /* ... */ ]
    }

    Note for Turbopack users: You must add a path alias to tsconfig.json so /videos imports resolve correctly:

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

    5. Update dev script

    Add the watch command to your package.json to sync videos during development:

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

    6. Sync videos

    To upload and process a video file (e.g., videos/sample-video.mp4), run:

    npx next-video sync
  5. Use remote videos with next-video

    main

    You can use videos already hosted on a remote server (e.g., AWS S3) by importing the URL directly into the <Video> component. Refreshing the page after import will trigger the creation of a local JSON file in the /videos folder and start the upload/processing sync.

    Direct URL Import

    import Video from 'next-video';
    import awesomeVideo from 'https://www.mydomain.com/remote-video.mp4';
    
    export default function Page() {
      return <Video src={awesomeVideo} />;
    }

    Using a string source URL via API route

    If you don't have the URL available at import time, you can use a string URL by setting up a request handler API route.

    App router (Next.js >=13):

    // app/api/video/route.js
    export { GET } from 'next-video/request-handler';

    Pages router (Next.js):

    // pages/api/video/[[...handler]].js
    export { default } from 'next-video/request-handler';

    Then use the string URL in your component:

    import Video from 'next-video';
    
    export default function Page() {
      return <Video src="https://www.mydomain.com/remote-video.mp4" />;
    }
    IMPORTANT

    The /api/video route is unauthenticated by default. It only serves asset descriptors within the configured video folder (default videos/). Requests outside this folder return 404. Consider adding authentication if exposing this in production.

    import Video from 'next-video';
    import awesomeVideo from 'https://www.mydomain.com/remote-video.mp4';
    
    export default function Page() {
      return <Video src={awesomeVideo} />;
  6. Enable public access for Cloudflare R2 Buckets

    main

    By default, Cloudflare R2 Buckets are not publicly accessible. To enable public access for next-video, you must do one of the following:

    1. Configure the Bucket for Public Access:

      • Provide a bucket name in the provider configuration and ensure it is configured for public access in Cloudflare.
      • OR specify the public URL in the provider configuration using the bucketUrlPublic key.
    2. Provide a Cloudflare API Key:

      • Set the environment variable R2_CF_API_TOKEN with an API Token that has R2 Admin read & write permissions.
      • This allows the provider to automatically enable public access and retrieve the public URL via the Cloudflare API.
  7. Automatic Setup with next-video init

    main

    The easiest way to set up next-video is to run the initialization command in your Next.js project root. This command automates dependency installation, directory creation, configuration updates, and environment variable setup.

    npx -y next-video init

    What this command does:

    • Installs next-video as a dependency.
    • Creates a /videos directory and updates .gitignore to exclude video files.
    • Updates next.config.js (or .mjs).
    • Adds TypeScript types for video imports.
    • Adds next-video sync -w to your package.json dev script.
    • Sets up Mux credentials in .env.local for remote storage.
    • Adds a sample video and demo page.
    npx -y next-video init
  8. Configure hosting and processing providers

    main

    You can switch the default provider (Mux) by adding a provider option in your next-video configuration within next.config.js. Some providers require additional settings via providerConfig.

    Configuration Example

    // next.config.js
    const { withNextVideo } = require('next-video/process');
    
    /** @type {import('next').NextConfig} */
    const nextConfig = {};
    
    module.exports = withNextVideo(nextConfig, {
      provider: 'backblaze',
      providerConfig: {
        backblaze: { endpoint: 'https://s3.us-west-000.backblazeb2.com' },
      },
    });

    Supported Providers and Environment Variables

    ProviderEnvironment varsProvider config
    mux (default)MUX_TOKEN_ID, MUX_TOKEN_SECRETvideoQuality ('basic' | 'plus' | 'premium')
    vercel-blobBLOB_READ_WRITE_TOKEN-
    backblazeBACKBLAZE_ACCESS_KEY_ID, BACKBLAZE_SECRET_ACCESS_KEYendpoint, bucket
    amazon-s3AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEYendpoint, bucket, region
    cloudflare-r2R2_ACCESS_KEY_ID, R2_SECRET_ACCESS_KEY, R2_CF_API_TOKENbucket, bucketUrlPublic
    const { withNextVideo } = require('next-video/process');
    
    /** @type {import('next').NextConfig} */
    const nextConfig = {};
    
    module.exports = withNextVideo(nextConfig, {
      provider: 'backblaze',
      providerConfig: {
        backblaze: { endpoint: 'https://s3.us-west-000.backblazeb2.com' },
      },
    });
  9. Customize the <Video> component theme

    main

    You can change the player's visual theme by passing the theme prop to the <Video> component. This accepts themes from libraries like player.style.

    import Video from 'next-video';
    import Instaplay from 'player.style/instaplay/react';
    import awesomeVideo from '/videos/awesome-video.mp4';
    
    export default function Page() {
      return <Video src={awesomeVideo} theme={Instaplay} />;
    }
    import Video from 'next-video';
    import Instaplay from 'player.style/instaplay/react';
    import awesomeVideo from '/videos/awesome-video.mp4';
    
    export default function Page() {
      return <Video src={awesomeVideo} theme={Instaplay} />;
  10. Style the default player with CSS variables

    main

    The default player uses the Sutro theme from Media Chrome. You can override its appearance using CSS variables passed via the style prop on the <Video> component.

    Commonly used variables:

    • --media-primary-color: Color of control icons.
    • --media-secondary-color: Background color of controls on hover.
    • --media-accent-color: Color of volume and time sliders.

    For a full list, refer to the Media Chrome styling docs.

    import Video from 'next-video';
    import getStarted from '/videos/get-started.mp4';
    
    export default function Page() {
      return <Video src={getStarted} style={{
        '--media-primary-color': '#fdaff3',
        '--media-secondary-color': '#ff0088',
        '--media-accent-color': '#42ffe0',
      }} />;
    }
  11. Customize asset metadata storage hooks

    main

    By default, asset metadata is stored in JSON files in the /videos directory. You can customize this behavior (e.g., to use a database) by implementing loadAsset, saveAsset, and updateAsset hooks in a next-video.mjs configuration file.

    Implementation Example

    // next-video.mjs
    import { NextVideo } from 'next-video/process';
    import path from 'node:path';
    import { mkdir, readFile, writeFile } from 'node:fs/promises';
    
    export const { GET, POST, handler, withNextVideo } = NextVideo({
      loadAsset: async function (assetPath) {
        const file = await readFile(assetPath);
        return JSON.parse(file.toString());
      },
      saveAsset: async function (assetPath, asset) {
        try {
          await mkdir(path.dirname(assetPath), { recursive: true });
          await writeFile(assetPath, JSON.stringify(asset), { flag: 'wx' });
        } catch (err) {
          if (err.code === 'EEXIST') return;
          throw err;
        }
      },
      updateAsset: async function (assetPath, asset) {
        await writeFile(assetPath, JSON.stringify(asset));
      },
    });

    Then, wrap your Next.js config with this custom file:

    // next.config.mjs
    import { withNextVideo } from './next-video.mjs';
    
    /** @type {import('next').NextConfig} */
    const nextConfig = {};
    
    export default withNextVideo(nextConfig);
    import { NextVideo } from 'next-video/process';
    import path from 'node:path';
    import { mkdir, readFile, writeFile } from 'node:fs/promises';
    
    export const { GET, POST, handler, withNextVideo } = NextVideo({
      loadAsset: async function (assetPath) {
        const file = await readFile(assetPath);
        const asset = JSON.parse(file.toString());
        return asset;
      },
      saveAsset: async function (assetPath, asset) {
        try {
          await mkdir(path.dirname(assetPath), { recursive: true });
          await writeFile(assetPath, JSON.stringify(asset), {
            flag: 'wx',
          });
        } catch (err) {
          if (err.code === 'EEXIST') {
            return;
          }
          throw err;
        }
      },
      updateAsset: async function (assetPath, asset) {
        await writeFile(assetPath, JSON.stringify(asset));
      },
    });
  12. Use the <Player> or <BackgroundVideo> components directly

    main

    If you want to use the video player without the automatic upload and processing features, you can import the player components directly.

    Player component

    Use <Player> or <BackgroundPlayer> for standard video playback.

    import Player from 'next-video/player';
    // or
    import BackgroundPlayer from 'next-video/background-player';
    
    export default function Page() {
      return (
        <Player
          src="https://www.mydomain.com/remote-video.mp4"
          poster="https://www.mydomain.com/remote-poster.webp"
          blurDataURL="data:image/webp;base64,UklGRlA..."
        />
      );
    }

    BackgroundVideo component

    Use <BackgroundVideo> for background videos with no player controls. This component is optimized for background usage and reduces JS payload.

    import BackgroundVideo from 'next-video/background-video';
    import getStarted from '/videos/country-clouds.mp4?thumbnailTime=0';
    
    export default function Page() {
      return (
        <BackgroundVideo src={getStarted}>
          <h1>next-video</h1>
        </BackgroundVideo>
      );
    }
    import Player from 'next-video/player';
    
    export default function Page() {
      return (
        <Player
          src="https://www.mydomain.com/remote-video.mp4"
          poster="https://www.mydomain.com/remote-poster.webp"
          blurDataURL="data:image/webp;base64,UklGRlA..."
        />
      );
    }