Skip to content

VideoEmbed

SM · 950B gzip budget

Lazy-loaded video embed with privacy-friendly placeholder.

Category
Media & layout
Budget
sm, single-purpose control
External runtime
React / React DOM only
axe fixtureSSR rendersource scanbundle budget

Preview

Live fixture
Preview locale

Try the component here. Locale-aware formatting and direction follow the selected language; example content and labels are not automatically translated. Overlay demos use interactive launchers.

Install

npx shadcn@latest add https://gear5-ui.vercel.app/r/video-embed.json

Copies the source into your project.

New here? Set up Tailwind and import aliases first →

Source & props

Edit on GitHub ↗

Prop types and inline documentation are included below. This is repository source; the installer rewrites shared imports for your project.

"use client";

import React, { useCallback, useState } from "react";

export interface VideoEmbedProps {
  src?: string;
  title?: string;
  label?: string;
}

export function VideoEmbed({
  src,
  title = "Embedded video",
  label = "Video player",
}: VideoEmbedProps) {
  const [playing, setPlaying] = useState(false);

  const handlePlay = useCallback(() => {
    setPlaying(true);
  }, []);

  return (
    <div
      role="region"
      aria-label={label}
      style={{
        position: "relative",
        width: "100%",
        paddingTop: "56.25%",
        borderRadius: "0.75rem",
        overflow: "hidden",
        backgroundColor: "#000",
      }}
    >
      {playing ? (
        <iframe
          src={src}
          title={title}
          allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
          allowFullScreen
          style={{
            position: "absolute",
            top: 0,
            left: 0,
            width: "100%",
            height: "100%",
            border: "none",
          }}
        />
      ) : (
        <button
          onClick={handlePlay}
          aria-label={`Play ${title}`}
          style={{
            position: "absolute",
            top: 0,
            left: 0,
            width: "100%",
            height: "100%",
            border: "none",
            backgroundColor: "rgba(0,0,0,0.6)",
            cursor: "pointer",
            display: "flex",
            flexDirection: "column",
            alignItems: "center",
            justifyContent: "center",
            gap: "0.75rem",
          }}
        >
          <div
            aria-hidden="true"
            style={{
              width: 68,
              height: 48,
              borderRadius: "0.75rem",
              backgroundColor: "rgba(255,0,0,0.85)",
              display: "flex",
              alignItems: "center",
              justifyContent: "center",
            }}
          >
            <svg width="24" height="24" viewBox="0 0 24 24" fill="#fff">
              <path d="M8 5v14l11-7z" />
            </svg>
          </div>
          <span
            aria-hidden="true"
            style={{
              color: "#fff",
              fontSize: "0.875rem",
              fontWeight: 500,
            }}
          >
            {title}
          </span>
        </button>
      )}
    </div>
  );
}

What CI checks

Quality contract
  • Bundled, minified and gzipped against its tier budget
  • Audited by axe in the state previewed above
  • Rendered through react-dom/server with no browser globals
  • Scanned for network calls, dangerous sinks, and unguarded animation