Vercel is optimized for frontend applications, but it can also serve static video files efficiently when configured properly. While it's not designed for large-scale video streaming, it can handle lightweight use cases like hosting short clips or product demos. Understanding how Vercel handles static assets, caching, and bandwidth limits is key to building a reliable video experience.

Hosting Videos Externally for Vercel Projects

Videos should not be uploaded directly to Vercel due to file size and bandwidth limitations. Instead, consider using one of these 2 approaches:

Object Storage: Services like AWS S3 or GCP Storage let you store video files and serve them through a CDN. This gives you control over encoding, caching, and playback.

Video Platforms: Providers like YouTube handle storage, transcoding, adaptive streaming, and player features.

Choose object storage if you need full control, or a video platform if you want an immediate streaming-ready pipeline.

Streaming Video in a Vercel Application

Videos should be streamed (not served as full downloads) to avoid bandwidth waste and provide smooth playback. Streaming protocols like DASH split media into segments and adjust quality. Platforms such as YouTube generate .mpd (DASH) manifests automatically.

For raw storage workflows, use a client-side player like hls.js.

Example: Playing HLS in a Next.js Component

code
import { useEffect, useRef } from "react";
import Hls from "hls.js";

export default function VideoPlayer({ src }) {
const videoRef = useRef(null);

useEffect(() => {
if (Hls.isSupported()) {
const hls = new Hls();
hls.loadSource(src); // HLS manifest URL (.m3u8)
hls.attachMedia(videoRef.current);
}
}, [src]);

return <video ref={videoRef} controls width="640" height="360" />;
}

Explanation:

  • const videoRef = useRef(null); : videoRef holds a reference to the <video> DOM element.
  • Hls.isSupported(): Checks if the environment supports MSE/HLS playback (whether Hls.js can run).
  • const hls = new Hls();: Creates an Hls.js instance to manage manifest loading and segment fetching.
  • hls.attachMedia(videoRef.current);: Binds the Hls.js instance to the actual HTML <video> element so playback occurs.
  • <video ref={videoRef} controls width="640" height="360" />;: Native HTML5 video element with a ref, built-in controls, and fixed dimensions.
Streaming Video

Integrating Video with a Next.js App on Vercel

Integration connects externally hosted videos to your Vercel frontend to enable adaptive streaming and reduce load on your deployment. While Vercel can serve small static video files, it's not designed for hosting or streaming large video content at scale.

Embedding Videos from a Video Platform

For platforms like YouTube, you can use their embed URLs:

code
export default function YouTubeEmbed() {
return (
<iframe
width="560"
height="315"
src="https://www.youtube.com/embed/VIDEO_ID" // hosted video URL
title="YouTube video player"
frameBorder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
/>
);
}

Example:

  • src="...": The video"s hosted URL.
  • allow="...": Grants permissions for autoplay, PiP, and encrypted media.
  • <iframe>: embeds the player in your Next.js page without downloading the video to Vercel.

Streaming Videos from Object Storage

For self-hosted videos in S3, R2, or GCP Storage, use adaptive streaming with HLS/DASH:

code
import { useEffect, useRef } from "react";
import Hls from "hls.js";

export default function VideoPlayer({ src }) {
const videoRef = useRef(null);

useEffect(() => {
if (Hls.isSupported()) {
const hls = new Hls();
hls.loadSource(src); // URL of .m3u8 manifest from storage/CDN
hls.attachMedia(videoRef.current);
}
}, [src]);

return <video ref={videoRef} controls width="640" height="360" />;
}

Example:

  • videoRef: references the <video> DOM element.
  • Hls.loadSource(src): points to the HLS manifest hosted externally.
  • hls.attachMedia(videoRef.current): binds the HLS stream to the video element for playback.

Fetching Signed URLs from Storage

To securely stream private videos, generate signed URLs via a Vercel API route:

code
// pages/api/video.js
import { getSignedUrl } from "@/lib/storage";

export default async function handler(req, res) {
const { id } = req.query;
const url = await getSignedUrl(id); // temporary signed URL from S3/R2
res.json({ url });
}

Then consume the URL in a video component:

code
import { useState, useEffect } from "react";

export default function VideoFromAPI() {
const [url, setUrl] = useState(null);

useEffect(() => {
fetch("/api/video?id=123")
.then((res) => res.json())
.then((data) => setUrl(data.url));
}, []);

return url ? <video src={url} controls width="640" height="360" /> : null;
}

Example:

  • Fetches a temporary signed URL from your storage provider.
  • Enables secure playback without exposing private video files.
  • Works with both direct download and HLS/DASH streaming if the URL points to a manifest.

Setup for Hosting and Streaming on Vercel

A production-ready workflow separates video storage, streaming, and frontend delivery. This avoids Vercel"s file size/bandwidth limits while ensuring scalable playback.

Step 1: Host videos externally using object storage or a video platform.

Step 2: Enable CDN delivery or rely on the platform"s built-in streaming.

Step 3: Integrate into Next.js with an embed, a video player library, or signed URL APIs.

Step 4: Deploy on Vercel to handle the frontend and serverless API logic.

This ensures scalability, avoids Vercel"s bandwidth and size limits, and delivers adaptive streaming in production.