Video Embed
Responsive video embed for YouTube and other providers.
Example Video
Installation
pnpm dlx shadcn@latest add https://ui.vllnt.com/r/video-embed.jsonbash
Code
"use client";
import { useState } from "react";
import { Play, Video } from "lucide-react";
import { cn } from "../../lib/utils";
export type VideoEmbedProps = {
aspectRatio?: "1/1" | "4/3" | "16/9";
src: string;
thumbnail?: string;
title: string;
type?: "custom" | "vimeo" | "youtube";
};
function getEmbedUrl(source: string, type: string): string {
if (type === "youtube") {
const videoId =
/(?:youtube\.com\/(?:watch\?v=|embed\/)|youtu\.be\/)([^&?]+)/.exec(
source,
)?.[1];
return videoId ? `https://www.youtube.com/embed/${videoId}` : source;
}
if (type === "vimeo") {
const videoId = /vimeo\.com\/(\d+)/.exec(source)?.[1];
return videoId ? `https://player.vimeo.com/video/${videoId}` : source;
}
return source;
}
export function VideoEmbed({
aspectRatio = "16/9",
src,
thumbnail,
title,
type = "youtube",
}: VideoEmbedProps) {
const [isPlaying, setIsPlaying] = useState(false);
const embedUrl = getEmbedUrl(src, type);
const aspectClass =
aspectRatio === "16/9"
? "aspect-video"
: aspectRatio === "4/3"
? "aspect-[4/3]"
: "aspect-square";
return (
<div className="my-6">
<div
className={cn(
"relative rounded-lg overflow-hidden bg-muted border",
aspectClass,
)}
>
{isPlaying ? (
<iframe
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
className="absolute inset-0 w-full h-full"
src={`${embedUrl}?autoplay=1`}
title={title}
/>
) : (
<button
className="absolute inset-0 w-full h-full flex items-center justify-center group"
onClick={() => {
setIsPlaying(true);
}}
type="button"
>
{thumbnail ? (
<img
alt={title}
className="absolute inset-0 w-full h-full object-cover"
src={thumbnail}
/>
) : (
<div className="absolute inset-0 bg-gradient-to-br from-muted to-muted-foreground/20" />
)}
<div className="relative z-10 flex h-16 w-16 items-center justify-center rounded-full bg-primary text-primary-foreground shadow-lg transition-transform group-hover:scale-110">
<Play className="h-6 w-6 ml-1" />
</div>
</button>
)}
</div>
<p className="mt-2 text-sm text-center text-muted-foreground flex items-center justify-center gap-1">
<Video className="h-4 w-4" />
{title}
</p>
</div>
);
}
typescript