While Vercel excels at frontend delivery, it's not designed for hosting or streaming large media assets like Video on Demand (VOD). To ensure fast, reliable video playback, you should offload video storage, processing, and delivery to specialized services. This will allow Vercel to focus on delivering dynamic user interfaces.
Choose the Right Storage and Delivery Setup
Avoid storing .mp4 files in your Vercel project or static assets. Doing so increases build times and degrades performance. Instead, use a dedicated media pipeline:
| Component | Recommended Tool | Purpose |
| Storage | Google Cloud Storage, Cincopa | Store raw or encoded video assets. |
| Transcoding | FFmpeg, Cincopa | Convert videos to adaptive formats (e.g., HLS). |
| CDN Delivery | CloudFront, Cincopa | Distribute video segments with low latency. |
Implement Adaptive Streaming with HLS
To improve playback reliability across varying bandwidth conditions, transcode each video into multiple resolutions (360p, 720p, 1080p, etc.) and generate an HLS master playlist (.m3u8).
FFmpeg Example: Creating HLS Segments
ffmpeg -i input.mp4 \
-preset fast -g 48 -sc_threshold 0 \
-map 0:v -map 0:a \
-s:v:0 1920x1080 -b:v:0 5000k \
-s:v:1 1280x720 -b:v:1 2800k \
-s:v:2 854x480 -b:v:2 1400k \
-s:v:3 640x360 -b:v:3 800k \
-c:v libx264 -c:a aac -f hls \
-hls_time 6 -hls_playlist_type vod \
-hls_segment_filename "output_%v/data%02d.ts" \
-master_pl_name master.m3u8 \
-var_stream_map "v:0,a:0 v:1,a:0 v:2,a:0 v:3,a:0" output_%v.m3u8
Explanations:
- -g 48: Sets keyframe interval (important for smooth switching)
- -sc_threshold 0: Disables scene change detection to keep GOP size consistent
- -s:v:N and -b:v:N: Set resolution and bitrate for each rendition
- -f hls: Output format set to HLS
- -hls_time 6: Splits video into 6-second chunks
- -var_stream_map: Maps video and audio renditions to the master playlist
Once transcoding is done, upload the segments and playlists to your CDN for delivery.
Optimize Video Player Performance in Next.js
Use getStaticProps with ISR (Incremental Static Regeneration) to pre-render video pages while keeping them up-to-date without full redeploys.
Example: Static Generation with ISR
// pages/video/[id].js
export async function getStaticProps({ params }) {
const res = await fetch(`https://your-api.com/videos/${params.id}`);
const video = await res.json();
return {
props: {
videoUrl: video.hls_url,
},
revalidate: 60, // Revalidate every 60 seconds
};
}
Explanations:
- getStaticProps: Generates static video pages at build time or on-demand
- revalidate: 60: Keeps content fresh every minute
- Reduces server load and improves performance on frequently accessed videos.
Edge Caching and Revalidation
Use edge caching for video metadata and API responses to reduce latency and improve scalability.
Example: Set Cache-Control Headers in API Routes
// pages/api/videos/[id].ts
export default async function handler(req, res) {
const video = await getVideoFromCMS(req.query.id);
res.setHeader('Cache-Control', 's-maxage=300, stale-while-revalidate');
res.status(200).json(video);
}
Explanations:
- s-maxage=300: CDN caches the response for 5 minutes.
- stale-while-revalidate: Allows serving cached data while a new version is fetched in the background.
Tip: Configure your CDN to cache HLS segments for longer durations (e.g., 1 year) since they don't change after upload.

