Discover solutions for audio & video tag issues in Lovable. Learn effective embedding techniques and media best practices.

Book a call with an Expert
Starting a new venture? Need to upgrade your web app? RapidDev builds application with your growth in mind.
Audio/video tags fail to load in Lovable most often because the browser can’t fetch the media: wrong public path, blocked by CORS or mixed-content, missing byte-range (Accept-Ranges) support for seeking, or using private/signed URLs without configuring Secrets and preview access in Lovable. Some fixes are pure frontend edits you can do inside Lovable; others require changing storage bucket CORS or response headers in the cloud provider (those steps are outside Lovable).
Prompt: Verify and fix local asset path and attributes — paste this into Lovable chat to update the component that renders the media.
// Edit src/components/MediaPlayer.tsx
// Update video/audio tags to use absolute public paths, preload metadata, controls, and crossOrigin
Replace file src/components/MediaPlayer.tsx with this content:
import React from 'react';
export default function MediaPlayer({src, type}) {
return (
<div>
{/* use /media/... if files are in public/media */}
<video src={src} controls preload="metadata" crossOrigin="anonymous">
{/* fallback */}
</video>
<audio src={src} controls preload="metadata" crossOrigin="anonymous"></audio>
</div>
);
}
Prompt: Use Secrets for private storage (Supabase/S3) and fetch a signed URL — add secrets in Lovable then create helper
// In Lovable Secrets UI add SUPABASE_URL and SUPABASE_KEY (name them exactly)
// Create src/lib/storageClient.ts
// This file should fetch a signed URL server-side or client-side using the secret
export async function getSignedUrl(path) {
// // Use fetch to Supabase REST or storage endpoint using SUPABASE_KEY from Secrets
// // Return a usable URL for <video src=...>
}
Prompt: Test in Preview and Publish — after edits, run Lovable Preview to verify console/network. If video requests show CORS or 403 errors, follow the next prompt.
// Use Preview in Lovable and open DevTools Network tab
// Look for 4xx/5xx, CORS errors, or 200 without Accept-Ranges
// Report the exact network error back here and I'll suggest next steps
This prompt helps an AI assistant understand your setup and guide you through the fix step by step, without assuming technical knowledge.
Use HTML5
// Create file src/components/MediaPlayer.tsx with this content:
// A minimal React component that supports video and audio files via a src prop.
import React from 'react';
type Props = {
src: string;
kind?: 'video' | 'audio';
poster?: string;
controls?: boolean;
autoPlay?: boolean;
loop?: boolean;
};
export default function MediaPlayer({src, kind = 'video', poster, controls = true, autoPlay=false, loop=false}: Props) {
if (kind === 'audio') {
return <audio src={src} controls={controls} autoPlay={autoPlay} loop={loop} />;
}
return (
<video src={src} poster={poster} controls={controls} autoPlay={autoPlay} loop={loop} style={{maxWidth: '100%'}} />
);
}
// Update src/App.tsx - add the imports and example usage inside the main render/return
import MediaPlayer from './components/MediaPlayer';
// add somewhere in the JSX:
{/* // Video from public/media/video.mp4 */}
<MediaPlayer kind="video" src="/media/video.mp4" poster="/media/video-poster.jpg" />
{/* // Audio from public/media/song.mp3 */}
<MediaPlayer kind="audio" src="/media/song.mp3" />
// Create file src/components/EmbedPlayer.tsx
import React from 'react';
type Props = { url: string; title?: string; height?: number; width?: string; };
export default function EmbedPlayer({url, title='Embedded media', height=360, width='100%'}: Props) {
return <iframe src={url} title={title} width={width} height={height} frameBorder="0" allowFullScreen />;
}
// Then import and use in src/App.tsx:
// YouTube embed example (use the embed URL, not the watch URL)
<EmbedPlayer url="https://www.youtube.com/embed/VIDEO_ID" />
// Create public/media/README.txt with instructions:
// Upload your .mp4/.mp3 files into this folder using Lovable's Files panel.
// Example filenames used in the app: video.mp4, video-poster.jpg, song.mp3
Summary: Paste the prompts above into Lovable’s chat to create components and update files, upload your media via the Files panel into public/media, and use Lovable Secrets for build-time environment values when needed.
The simplest, most reliable approach in Lovable is to keep media assets off the code repo (use a CDN or storage like Supabase public buckets or signed URLs) and implement a small, accessibility-forward MediaPlayer component with lazy loading, proper MIME sources, CORS awareness, poster/thumbnail fallback, and server-side signed URLs for private content. Use Lovable’s Chat Mode to add the component files, the Secrets UI to store keys, Preview to test, and GitHub export only if you need to install new packages or add backend dependencies (that part is outside Lovable).
Prompt A — create a simple accessible MediaPlayer component (client-only)
// Create src/components/MediaPlayer.tsx
// Implement an accessible, lazy-loading HTML5 player with poster, multiple sources & captions
// Use this component from src/App.tsx inside your main view.
export default function MediaPlayer({ sources, poster, captions, alt }) {
// sources: [{src: "https://cdn.example/video.mp4", type: "video/mp4"}, ...]
// captions: [{srclang:"en", src:"/captions/en.vtt", label:"English"}]
return (
<div className="media-player" role="region" aria-label={alt || "Media player"}>
<video
controls
preload="metadata"
poster={poster}
crossOrigin="anonymous"
style={{maxWidth: "100%", height: "auto"}}
>
{sources.map(s => <source key={s.src} src={s.src} type={s.type} />)}
{captions && captions.map(c=> <track key={c.src} kind="subtitles" src={c.src} srcLang={c.srclang} label={c.label} default={c.default} />)}
{/* Fallback text */}
Your browser does not support the video tag.
</video>
</div>
);
}
Prompt B — add a small CSS file for responsive media
// Create src/styles/media-player.css
// Basic responsive rules, adjust to your design system
.media-player video {
width: 100%;
height: auto;
display: block;
background: #000;
border-radius: 6px;
}
Prompt C — use Secrets UI + Next.js API for signed URLs (private media)
// Step 1: In Lovable, open the Secrets panel and add:
// Name: SUPABASE_SERVICE_KEY
// Value: <your supabase service role key or server key>
// Name: SUPABASE_URL
// Value: <your supabase project URL>
// Step 2 (If your app is Next.js): create src/pages/api/signed-url.ts
// This endpoint will return a time-limited signed URL for a storage object.
// Note: installing @supabase/supabase-js requires npm install — see the "outside Lovable" note below.
import { createClient } from "@supabase/supabase-js";
// // // // // // // // // // // // // // // // // // // // // // // // //
// // This file requires @supabase/supabase-js to be installed on the server
// // If not installed yet, export to GitHub and run npm install (outside Lovable)
// // // // // // // // // // // // // // // // // // // // // // // // //
const supabase = createClient(process.env.SUPABASE_URL, process.env.SUPABASE_SERVICE_KEY);
export default async function handler(req, res) {
const { bucket, path } = req.query;
const { data, error } = await supabase.storage.from(bucket).createSignedUrl(path, 60); // 60s
if (error) return res.status(500).json({ error: error.message });
res.json({ url: data.signedURL });
}
From startups to enterprises and everything in between, see for yourself our incredible impact.
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.