Skip to main content
RapidDev - Software Development Agency
v0-issues

Adding video and audio elements to v0 projects

Adding video and audio to V0 projects requires using HTML5 media tags or iframe embeds within client components. Place local media files in the public/ directory and reference them with absolute paths. For YouTube or Vimeo, use the next/script component or iframe embeds. The most common issue is media failing to play because the component is missing the 'use client' directive, which is required for interactive media controls.

Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate6 min read15-25 minutesV0 with Next.js App Router, HTML5 media APIsMarch 2026RapidDev Engineering Team
TL;DR

Adding video and audio to V0 projects requires using HTML5 media tags or iframe embeds within client components. Place local media files in the public/ directory and reference them with absolute paths. For YouTube or Vimeo, use the next/script component or iframe embeds. The most common issue is media failing to play because the component is missing the 'use client' directive, which is required for interactive media controls.

Why video and audio elements break in V0 apps

V0 generates server-rendered Next.js components by default. HTML5 video and audio elements with interactive controls (play, pause, volume) require browser APIs that only work in client components. When V0 places media elements in a Server Component without the 'use client' directive, the controls render but do not respond to user interaction. Additionally, media files referenced with relative paths or incorrect public/ directory structure return 404 errors after deployment.

  • Missing 'use client' directive on components with interactive media controls
  • Media files not placed in the public/ directory or referenced with relative paths
  • YouTube/Vimeo embeds blocked by Content Security Policy headers on Vercel
  • Autoplay blocked by browser policies — requires muted attribute for autoplay to work
  • Large video files causing slow page loads without lazy loading or streaming

Error messages you might see

GET /videos/intro.mp4 404 (Not Found)

The video file is not in the public/ directory or the path does not match. Ensure the file is at public/videos/intro.mp4 and referenced as /videos/intro.mp4.

NotAllowedError: The request is not allowed by the user agent or the platform in the current context

The browser blocked autoplay because the video is not muted. Add the muted attribute to enable autoplay, or remove autoplay and let users click to play.

Refused to frame 'https://www.youtube.com/' because it violates the following Content Security Policy directive

Your Vercel deployment has Content Security Policy headers that block YouTube iframe embeds. Add youtube.com to the frame-src directive in your security headers.

Before you start

  • A V0 project where you need video or audio elements
  • Media files ready (local MP4/MP3 or external URLs like YouTube)
  • Basic understanding of HTML5 media elements

How to fix it

1

Add the 'use client' directive for interactive media components

Next.js App Router components are Server Components by default. Video and audio controls, event handlers like onPlay or onPause, and programmatic playback require client-side JavaScript.

Add 'use client' at the top of any component file that contains video or audio elements with interactive controls or JavaScript event handlers.

Before
typescript
// app/components/VideoPlayer.tsx
// Missing 'use client' — controls won't work
import { useRef } from 'react';
export function VideoPlayer() {
const videoRef = useRef<HTMLVideoElement>(null);
return <video ref={videoRef} controls src="/videos/intro.mp4" />;
}
After
typescript
// app/components/VideoPlayer.tsx
'use client';
import { useRef } from 'react';
export function VideoPlayer() {
const videoRef = useRef<HTMLVideoElement>(null);
return (
<video
ref={videoRef}
controls
src="/videos/intro.mp4"
className="w-full rounded-lg"
/>
);
}

Expected result: Video controls (play, pause, volume, fullscreen) respond to user clicks in both V0 preview and production.

2

Place local media files in the public/ directory

Next.js serves static files from public/ at the root URL. Files outside this directory are not accessible via URL in production.

Create public/videos/ and public/audio/ directories in the V0 file explorer. Upload your media files there. Reference them with absolute paths starting with /.

Before
typescript
// WRONG — relative path or import
import introVideo from './assets/intro.mp4';
<video src={introVideo} />
After
typescript
// CORRECT — absolute path from public/
<video src="/videos/intro.mp4" controls className="w-full" />

Expected result: The media file loads from /videos/intro.mp4 in both the V0 preview and after Vercel deployment.

3

Embed YouTube or Vimeo videos using iframes

External video platforms handle streaming, adaptive quality, and bandwidth optimization. Embedding them avoids hosting large video files in your project.

Use an iframe with the embed URL format. Wrap it in a responsive container using Tailwind's aspect-ratio utilities.

Before
typescript
// Non-responsive, fixed-size embed
<iframe
src="https://www.youtube.com/watch?v=dQw4w9WgXcQ"
width="560"
height="315"
/>
After
typescript
<div className="aspect-video w-full">
<iframe
src="https://www.youtube.com/embed/dQw4w9WgXcQ"
title="Video title"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
className="w-full h-full rounded-lg"
/>
</div>

Expected result: The YouTube video displays responsively, maintaining 16:9 aspect ratio at all screen sizes.

4

Add background video with autoplay and muted attributes

Browsers block autoplay for unmuted videos to prevent unwanted noise. The muted attribute is required for autoplay to work across all browsers.

For hero sections or background videos, use autoPlay, muted, loop, and playsInline attributes. Consider adding a poster image as a fallback.

Before
typescript
// Autoplay will be blocked by the browser
<video autoPlay loop src="/videos/hero-bg.mp4" />
After
typescript
<video
autoPlay
muted
loop
playsInline
poster="/images/hero-poster.jpg"
className="absolute inset-0 w-full h-full object-cover -z-10"
>
<source src="/videos/hero-bg.mp4" type="video/mp4" />
<source src="/videos/hero-bg.webm" type="video/webm" />
</video>

Expected result: The background video plays automatically on page load, loops silently, and shows the poster image while loading.

Complete code example

app/components/MediaSection.tsx
1'use client';
2
3import { useState, useRef } from 'react';
4import { Button } from '@/components/ui/button';
5import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
6import { Play, Pause, Volume2, VolumeX } from 'lucide-react';
7
8export function MediaSection() {
9 const videoRef = useRef<HTMLVideoElement>(null);
10 const [isPlaying, setIsPlaying] = useState(false);
11 const [isMuted, setIsMuted] = useState(false);
12
13 const togglePlay = () => {
14 if (!videoRef.current) return;
15 if (isPlaying) {
16 videoRef.current.pause();
17 } else {
18 videoRef.current.play();
19 }
20 setIsPlaying(!isPlaying);
21 };
22
23 return (
24 <Card className="max-w-2xl mx-auto">
25 <CardHeader>
26 <CardTitle>Product Demo</CardTitle>
27 </CardHeader>
28 <CardContent className="space-y-4">
29 <div className="relative rounded-lg overflow-hidden">
30 <video
31 ref={videoRef}
32 src="/videos/demo.mp4"
33 poster="/images/demo-poster.jpg"
34 muted={isMuted}
35 className="w-full"
36 onEnded={() => setIsPlaying(false)}
37 />
38 </div>
39 <div className="flex gap-2">
40 <Button variant="outline" size="sm" onClick={togglePlay}>
41 {isPlaying ? <Pause className="h-4 w-4" /> : <Play className="h-4 w-4" />}
42 </Button>
43 <Button variant="outline" size="sm" onClick={() => setIsMuted(!isMuted)}>
44 {isMuted ? <VolumeX className="h-4 w-4" /> : <Volume2 className="h-4 w-4" />}
45 </Button>
46 </div>
47 </CardContent>
48 </Card>
49 );
50}

Best practices to prevent this

  • Always add 'use client' to components that use video/audio controls or event handlers
  • Store local media files in public/videos/ or public/audio/ and reference with absolute paths
  • Use the muted attribute for autoplay — browsers block unmuted autoplay universally
  • Provide multiple source formats (MP4 and WebM) for cross-browser compatibility
  • Add a poster attribute to video elements to show a preview image while the video loads
  • Use Tailwind's aspect-video class for responsive 16:9 video containers
  • For large videos, consider external hosting (YouTube, Vimeo, Cloudflare Stream) instead of public/ directory
  • Add loading='lazy' or use dynamic imports for below-the-fold media to improve page load speed

Still stuck?

Copy one of these prompts to get a personalized, step-by-step explanation.

ChatGPT Prompt

I need to add a video player component to my V0 Next.js App Router project. The video is hosted locally in the public/ directory. How do I create a custom player with play/pause controls using shadcn/ui components while handling the 'use client' requirement?

Frequently asked questions

Why won't my video autoplay in V0?

Browsers block autoplay for unmuted videos. Add the muted attribute alongside autoPlay. Also add playsInline for mobile Safari compatibility. The video must be muted for autoplay to work without user interaction.

How do I embed a YouTube video in a V0 project?

Use an iframe with the embed URL format: https://www.youtube.com/embed/VIDEO_ID. Wrap it in a div with className='aspect-video w-full' for responsive sizing. Use the embed URL, not the regular watch URL.

Should I host videos in public/ or use an external service?

For videos under 10MB, the public/ directory works fine. For larger videos, use YouTube, Vimeo, or Cloudflare Stream to avoid slow page loads and large deployment bundles. External services also handle adaptive bitrate streaming.

Why do my video controls not respond to clicks?

Your component is likely a Server Component. Add 'use client' at the top of the file. Without this directive, event handlers like onClick and refs like useRef do not work in Next.js App Router.

Can I add background music to my V0 app?

Yes, use an HTML5 audio element in a client component. However, autoplay for audio is blocked by browsers just like video. You must provide a user-triggered play button. Place the audio file in public/audio/ and reference it with an absolute path.

How do I make video embeds responsive in Tailwind?

Wrap the iframe in a div with className='aspect-video w-full'. Set the iframe to className='w-full h-full'. This maintains the 16:9 aspect ratio while filling the container width at any screen size.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your issue.

Book a free consultation

Need help with your Lovable project?

Our experts have built 600+ apps and can solve your issue fast. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.