# Adding Video and Audio Elements in Lovable Projects

- Tool: Lovable
- Difficulty: Beginner
- Fix time: ~5 min
- Compatibility: All Lovable versions (HTML5 video/audio supported in all modern browsers)
- Last updated: March 2026

## TL;DR

To add video or audio to a Lovable project, use HTML5 video and audio tags for self-hosted media files in the /public folder, or embed YouTube and Vimeo with responsive iframe containers. Browser autoplay policies block autoplay with sound — add the muted attribute alongside autoplay for videos that need to play automatically. Wrap video iframes in an aspect-ratio container using Tailwind's aspect-video class for responsive sizing.

## Why video and audio elements may not work correctly in Lovable

Adding media to a Lovable project is straightforward, but several browser behaviors can make videos and audio appear broken even when the code is correct. The most common issue is autoplay policy enforcement. Modern browsers (Chrome, Safari, Firefox) block autoplaying media that has sound. If you add autoplay to a video tag without also adding the muted attribute, the browser silently refuses to play it — no error appears, the video just sits frozen.

For self-hosted video and audio files, the file must be in the /public folder with the correct path. Lovable's Vite build serves static files from /public at the root URL, so a file at /public/videos/intro.mp4 is accessed as /videos/intro.mp4 in your code. Getting this path wrong results in a broken media player with no content.

Embedding YouTube or Vimeo videos requires an iframe rather than a video tag. The main issue with iframes is responsive sizing — without proper CSS, the embedded video either has a fixed pixel size that breaks on mobile or collapses to zero height. Tailwind's aspect-video class combined with a width-full container solves this reliably.

- Autoplay blocked by browser: video has autoplay but is missing the muted attribute
- Wrong file path: media file is not in /public or the src path does not match the actual file location
- Missing controls attribute: video/audio element renders but has no play button visible to the user
- Non-responsive iframe: YouTube/Vimeo embed has fixed dimensions that break on mobile screens
- CORS restrictions: media hosted on a different domain blocks loading due to cross-origin policy

## Error messages you might see

**DOMException: play() failed because the user didn't interact with the document first**

The browser blocked autoplay because the video has sound. Add the muted attribute to the video tag alongside autoplay. Browsers allow muted autoplay but block autoplay with audio until the user interacts with the page.

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

The video file is not at the expected path. Place your media file in the /public folder and use a root-relative path starting with / in the src attribute. For example, /public/videos/intro.mp4 is accessed as /videos/intro.mp4.

**Refused to display 'https://www.youtube.com/watch?v=...' in a frame because it set 'X-Frame-Options' to 'sameorigin'**

You are trying to embed a regular YouTube URL in an iframe. YouTube watch URLs block iframe embedding. Use the embed URL format instead: https://www.youtube.com/embed/VIDEO_ID.

## Before you start

- A Lovable project open in the editor
- Your video or audio file (for self-hosted) or the YouTube/Vimeo video URL (for embeds)
- For self-hosted files: access to the /public folder via GitHub or Dev Mode to upload media files

## How to fix it

### 1. Add a self-hosted video with HTML5 video tag

*The HTML5 video element is the standard way to play video files hosted in your project*

First, upload your video file to the /public/videos/ folder via GitHub or Dev Mode. Then add a video element in your component with the src pointing to the file using a root-relative path. Always include the controls attribute so users can play, pause, and seek. If you want the video to autoplay, you must also add the muted attribute — browsers block autoplay with sound.

Before:

```
<!-- Broken: autoplay without muted, missing controls -->
<video autoplay src="videos/intro.mp4">
  Your browser does not support the video tag.
</video>
{/* Video won't autoplay and user can't manually play either */}
```

After:

```
<!-- Working: muted autoplay with controls and responsive sizing -->
<video
  autoPlay
  muted
  controls
  loop
  playsInline
  className="w-full max-w-2xl rounded-lg"
  src="/videos/intro.mp4"
>
  Your browser does not support the video tag.
</video>
{/* Note: autoPlay is camelCase in JSX, playsInline prevents fullscreen on iOS */}
```

**Expected result:** The video plays automatically (muted) when the page loads and shows playback controls. The user can unmute manually using the controls.

### 2. Embed a YouTube or Vimeo video with a responsive iframe

*Third-party video platforms require iframe embeds with the correct embed URL format*

For YouTube, use the embed URL format: https://www.youtube.com/embed/VIDEO_ID (not the regular watch URL). For Vimeo, use https://player.vimeo.com/video/VIDEO_ID. Wrap the iframe in a container with Tailwind's aspect-video class to maintain the 16:9 aspect ratio and make it responsive. Add allow attributes for fullscreen and autoplay if desired.

Before:

```
<!-- Broken: using watch URL and fixed pixel dimensions -->
<iframe
  width="560"
  height="315"
  src="https://www.youtube.com/watch?v=dQw4w9WgXcQ"
></iframe>
{/* X-Frame-Options error and non-responsive on mobile */}
```

After:

```
<!-- Working: embed URL with responsive container -->
<div className="w-full max-w-3xl aspect-video">
  <iframe
    className="w-full h-full rounded-lg"
    src="https://www.youtube.com/embed/dQw4w9WgXcQ"
    title="Video player"
    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
    allowFullScreen
  />
</div>
{/* Responsive 16:9 container that works on all screen sizes */}
```

**Expected result:** The YouTube video loads and plays inside a responsive container that maintains 16:9 aspect ratio on desktop and mobile.

### 3. Add an audio player with HTML5 audio tag

*The audio element provides a built-in player UI for music, podcasts, or sound effects*

Add an audio element with the controls attribute and point the src to your audio file in /public. The browser renders a native audio player with play/pause, timeline, and volume controls. For multiple audio format support, use source elements inside the audio tag with different file formats (MP3, OGG, WAV) so the browser picks the one it supports.

Before:

```
<!-- Broken: audio file not in /public, no controls -->
<audio src="podcast.mp3" autoplay />
{/* No visible player, autoplay blocked, file 404 */}
```

After:

```
<!-- Working: file in /public, controls visible, fallback formats -->
<audio controls className="w-full max-w-md">
  <source src="/audio/podcast.mp3" type="audio/mpeg" />
  <source src="/audio/podcast.ogg" type="audio/ogg" />
  Your browser does not support the audio element.
</audio>
{/* Native player UI with play/pause, seek, and volume */}
```

**Expected result:** A visible audio player appears on the page. The user can click play to start listening, seek through the timeline, and adjust volume.

### 4. Create a reusable video embed component

*If your project uses multiple video embeds, a reusable component keeps the code DRY and consistent*

Create a component that accepts a video URL and optional props for autoplay and aspect ratio. This component handles the responsive container, correct iframe attributes, and URL format validation. Use it across multiple pages without repeating the iframe boilerplate. If you need advanced video features like custom controls, analytics tracking, or adaptive bitrate streaming, RapidDev's engineers can implement these patterns for you.

Before:

```
// Repeating iframe code on every page
<iframe src="https://www.youtube.com/embed/abc" width="560" height="315" />
<iframe src="https://www.youtube.com/embed/def" width="560" height="315" />
{/* Inconsistent sizing, no responsive behavior */}
```

After:

```
// Using the reusable VideoEmbed component
import VideoEmbed from "@/components/VideoEmbed";

<VideoEmbed url="https://www.youtube.com/embed/abc" title="Tutorial 1" />
<VideoEmbed url="https://www.youtube.com/embed/def" title="Tutorial 2" />
{/* Consistent responsive behavior across all embeds */}
```

**Expected result:** All video embeds on the site have consistent responsive sizing and proper iframe attributes. Adding new videos requires just one line of code.

## Complete code example

File: `src/components/VideoEmbed.tsx`

```typescript
import { cn } from "@/lib/utils";

interface VideoEmbedProps {
  url: string;
  title: string;
  className?: string;
  autoplay?: boolean;
}

// Reusable responsive video embed component
// Works with YouTube, Vimeo, and any iframe-compatible video URL
const VideoEmbed = ({ url, title, className, autoplay = false }: VideoEmbedProps) => {
  // Append autoplay parameter if requested
  const embedUrl = autoplay && !url.includes("autoplay")
    ? `${url}${url.includes("?") ? "&" : "?"}autoplay=1&mute=1`
    : url;

  return (
    <div className={cn("w-full aspect-video", className)}>
      <iframe
        className="w-full h-full rounded-lg"
        src={embedUrl}
        title={title}
        allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
        allowFullScreen
      />
    </div>
  );
};

// Example page using the component
const VideoPage = () => {
  return (
    <div className="container mx-auto py-8 space-y-8">
      <h1 className="text-3xl font-bold">Video Gallery</h1>

      {/* YouTube embed */}
      <VideoEmbed
        url="https://www.youtube.com/embed/dQw4w9WgXcQ"
        title="Product demo"
        className="max-w-3xl"
      />

      {/* Self-hosted video with native player */}
      <video
        controls
        playsInline
        className="w-full max-w-3xl rounded-lg"
        src="/videos/tutorial.mp4"
      >
        Your browser does not support the video tag.
      </video>
    </div>
  );
};

export default VideoEmbed;
```

## Best practices

- Always add muted alongside autoplay — browsers block autoplay with sound, but muted autoplay is allowed on all platforms
- Use the aspect-video Tailwind class on the iframe container to maintain 16:9 ratio responsively
- Add playsInline to video tags so mobile Safari does not force fullscreen playback
- Use YouTube/Vimeo embed URLs (youtube.com/embed/ID, player.vimeo.com/video/ID) not regular watch URLs in iframes
- Place self-hosted media files in /public/videos/ or /public/audio/ and reference with root-relative paths starting with /
- Always include the controls attribute on video and audio elements so users can play, pause, and seek
- Add a title attribute to iframes for accessibility — screen readers use it to describe the embedded content
- Compress video files before uploading — large files slow page load; consider using a video hosting service for files over 10MB

## Frequently asked questions

### Why is my video not autoplaying in Lovable?

Modern browsers block autoplay for videos with sound. Add both the autoPlay and muted attributes (camelCase in JSX) to your video tag. The browser allows muted autoplay on all platforms. Users can then unmute manually using the video controls.

### How do I embed a YouTube video in a Lovable project?

Use an iframe with the embed URL format: https://www.youtube.com/embed/VIDEO_ID (not the regular watch URL). Wrap the iframe in a div with Tailwind's aspect-video class for responsive 16:9 sizing. Add allowFullScreen and appropriate allow attributes.

### Where do I put video files in a Lovable project?

Place video and audio files in the /public folder, ideally in a subfolder like /public/videos/. Upload them via GitHub or Dev Mode. Reference them in your code with root-relative paths: /videos/filename.mp4. Vite serves /public contents at the site root.

### How do I make an embedded video responsive on mobile?

Wrap the iframe in a div with className="w-full aspect-video" and set the iframe to className="w-full h-full". Tailwind's aspect-video class maintains a 16:9 aspect ratio while the w-full class makes the container fill its parent width on any screen size.

### Can I use Vimeo videos in Lovable?

Yes. Use the Vimeo embed URL format: https://player.vimeo.com/video/VIDEO_ID. The iframe setup is identical to YouTube embeds. Wrap in an aspect-video container with the same allow attributes for fullscreen and autoplay.

### Why does my video show a CORS error?

This happens when loading a video file from a different domain that does not allow cross-origin requests. For self-hosted files, keep them in your /public folder. For external URLs, the server must include proper CORS headers. Using YouTube or Vimeo embed URLs avoids this issue entirely.

### What if I need advanced video features in my Lovable project?

If you need custom video controls, progress tracking, adaptive bitrate streaming, or DRM protection, these require additional libraries and configuration. RapidDev's engineers have implemented advanced media players across 600+ Lovable projects and can set this up for you.

---

Source: https://www.rapidevelopers.com/lovable-issues/adding-video-and-audio-elements-in-lovable-projects
© RapidDev — https://www.rapidevelopers.com/lovable-issues/adding-video-and-audio-elements-in-lovable-projects
