---
title: Video Text
description: A versatile component that masks a video inside text, creating a dynamic and engaging visual effect. It allows customization of font properties and video playback, powered by React, Tailwind CSS, and Framer Motion for optional animations.
---

<ComponentPreview name="video-text-demo" />

## Installation

<Tabs defaultValue="cli">

<TabsList>
  <TabsTrigger value="cli">CLI</TabsTrigger>
  <TabsTrigger value="manual">Manual</TabsTrigger>
</TabsList>
<TabsContent value="cli">

```bash
npx lightswind@latest add video-text
```

</TabsContent>

<TabsContent value="manual">

<Steps>

<Step>Copy and paste the following code into your project.</Step>

```tsx
"use client";

import { cn } from "@/components/lib/utils";
import React, { ReactNode, useEffect, useState } from "react";
import { motion, HTMLMotionProps } from "framer-motion";

export interface VideoTextProps {
  src: string;
  className?: string;
  autoPlay?: boolean;
  muted?: boolean;
  loop?: boolean;
  preload?: "auto" | "metadata" | "none";
  children: ReactNode;
  fontSize?: string | number;
  fontWeight?: string | number;
  textAnchor?: string;
  dominantBaseline?: string;
  fontFamily?: string;
  as?: "div" | "span" | "section" | "article" | "p" | "h1" | "h2" | "h3" | "h4" | "h5" | "h6";
}

export function VideoText({
  src,
  children,
  className = "",
  autoPlay = true,
  muted = true,
  loop = true,
  preload = "auto",
  fontSize = 20,
  fontWeight = "bold",
  textAnchor = "middle",
  dominantBaseline = "middle",
  fontFamily = "sans-serif",
  as = "div",
  ...motionProps
}: VideoTextProps & HTMLMotionProps<"div">) {
  const [svgMask, setSvgMask] = useState("");
  const content = React.Children.toArray(children).join("");

  useEffect(() => {
    const responsiveFontSize =
      typeof fontSize === "number" ? `${fontSize}vw` : fontSize;

    const newSvgMask = `<svg xmlns='http://www.w3.org/2000/svg' width='100%' height='100%'>
      <text x='50%' y='50%'
            font-size='${responsiveFontSize}'
            font-weight='${fontWeight}'
            text-anchor='${textAnchor}'
            dominant-baseline='${dominantBaseline}'
            font-family='${fontFamily}'
            fill='black'>${content}</text>
    </svg>`;
    setSvgMask(newSvgMask);
  }, [content, fontSize, fontWeight, textAnchor, dominantBaseline, fontFamily]);

  const validTags = ["div", "span", "section", "article", "p", "h1", "h2", "h3", "h4", "h5", "h6"] as const;
  type ValidTag = (typeof validTags)[number];

  const MotionComponent = motion[validTags.includes(as) ? as : "div"] as any;

  if (!svgMask) {
    return React.createElement(
      MotionComponent,
      {
        className: cn("relative size-full", className),
        ...motionProps,
      },
      <span className="sr-only">{content}</span>
    );
  }

  const dataUrlMask = `url("data:image/svg+xml,${encodeURIComponent(svgMask)}")`;

  return React.createElement(
    MotionComponent,
    {
      className: cn("relative overflow-hidden", className),
      ...motionProps,
    },
    <>
      <div
        className="absolute inset-0 flex items-center justify-center"
        style={{
          maskImage: dataUrlMask,
          WebkitMaskImage: dataUrlMask,
          maskSize: "contain",
          WebkitMaskSize: "contain",
          maskRepeat: "no-repeat",
          WebkitMaskRepeat: "no-repeat",
          maskPosition: "center",
          WebkitMaskPosition: "center",
          opacity: 1,
        }}
      >
        <video
          className="w-full h-full object-cover"
          autoPlay={autoPlay}
          muted={muted}
          loop={loop}
          preload={preload}
          playsInline
        >
          <source src={src} type="video/mp4" />
          Your browser does not support the video tag.
        </video>
      </div>
      <span className="sr-only">{content}</span>
    </>
  );
}

```

</Steps>

</TabsContent>

</Tabs>

## Usage

```tsx
import { VideoText } from '@/components/lightswind/video-text';
```

```tsx
import { VideoText } from '@/components/lightswind/video-text';

// Example with basic usage and default text properties
<VideoText src="/videos/ocean.mp4">
  EXPLORE
</VideoText>

// Example with custom font size, weight, and a simple Framer Motion animation
<VideoText
  src="/videos/cityscape.mp4"
  fontSize={18} // 18vw
  fontWeight={800}
  fontFamily="Arial Black, sans-serif"
  className="w-full h-[50vh]" // Example: full width, 50% viewport height
  autoPlay
  muted
  loop
  initial={{ opacity: 0, scale: 0.8 }}
  animate={{ opacity: 1, scale: 1 }}
  transition={{ duration: 1.5, ease: "easeOut" }}
>
  MOTION TEXT
</VideoText>

// Example with a different HTML element as the container
<VideoText
  as="h1"
  src="/videos/waterfall.mp4"
  fontSize={25}
  fontWeight="bold"
  textAnchor="start"
  dominantBaseline="hanging"
>
  NATURE'S BEAUTY
</VideoText>
```
