Retour au catalogue

About Video

Section about centree sur une video de presentation avec overlay play button.

aboutmedium Both Responsive a11y
bolddarkuniversalagencyeventcentered
Theme

How to build a video lightbox about section in React

This React about section shows a thumbnail with an animated play button; clicking it opens an iframe-based video modal with a scale+fade entrance powered by Framer Motion AnimatePresence. Stat items below the thumbnail enter one by one with a staggered whileInView animation.

  • Stack: React 18, Framer Motion 11, Lucide React (Play + X icons), Tailwind v4, ~175 lines.
  • State: a single boolean `isPlaying` toggles the modal; no external state library needed.
  • Accessible: the close button carries an aria-label; the iframe has a title attribute; clicking the backdrop also closes the modal.
  • Responsive: the thumbnail is aspect-video (16:9) with overflow-hidden; the modal caps at max-w-4xl and keeps its ratio on any screen.
  • Theming: all colors come from CSS custom properties (--color-background-dark, --color-accent, etc.), no hardcoded values.

About Video is a centered section that leads with a headline and badge, then surfaces a full-width video thumbnail with a prominent play button. Clicking the thumbnail opens a lightbox modal over the page. A row of stat counters beneath the video, each animated in on scroll, rounds out the social proof without requiring a separate section.

Anatomy

The section is divided into three vertical zones. At the top, a constrained max-w-2xl text block holds the optional badge (pill shape with border and accent color), the h2 title, and a subtitle paragraph. Below that, the thumbnail block fills max-w-5xl at 16:9 aspect-video with rounded corners and a semi-transparent overlay; the overlay always shows the play button, and the group-hover darkens it slightly while the button scales up. At the bottom, a 3-column grid renders the stat items with their large accent-colored values and muted labels.

How it works

Every visual block uses Framer Motion's `whileInView` with `viewport={{ once: true }}` so elements animate only the first time they scroll into view. The text header fades up (y: 20 to 0, opacity 0 to 1) in 500ms. The thumbnail enters with a slightly longer 600ms fade-up and a 100ms delay. Each stat item gets an additional staggered delay: `0.2 + i * 0.08`s, so they cascade naturally. The modal uses `AnimatePresence` so the exit animation (opacity 0, scale 0.9) actually runs before React unmounts the element, without `AnimatePresence`, the exit would be instant.

How to build it in React

  1. Build the thumbnail with a play overlay

    Render a relative-positioned div at aspect-video. Set the background image via an inline style on an absolute inset-0 div so the image never breaks the 16:9 ratio. Stack a second absolute inset-0 div with bg-black/30 for the overlay; add a group class on the wrapper and transition the overlay to bg-black/40 on hover.

    <motion.div
      className="relative mt-12 aspect-video rounded-[var(--radius-xl)] overflow-hidden cursor-pointer group"
      onClick={() => setIsPlaying(true)}
    >
      <div className="absolute inset-0 bg-cover bg-center"
        style={{ backgroundImage: `url(${thumbnailUrl})` }} />
      <div className="absolute inset-0 flex items-center justify-center bg-black/30 group-hover:bg-black/40 transition-colors">
        <div className="flex h-20 w-20 items-center justify-center rounded-full group-hover:scale-110 transition-transform duration-300"
          style={{ backgroundColor: "var(--color-accent)" }}>
          <Play className="h-8 w-8 ml-1" />
        </div>
      </div>
    </motion.div>
  2. Open and close the modal with AnimatePresence

    Wrap the modal JSX in `<AnimatePresence>` and gate it with `{isPlaying && videoUrl && ...}`. Give the backdrop a motion.div with opacity 0 to 1 enter / 0 exit. The inner panel uses scale 0.9 to 1 enter / 0.9 exit. Clicking the backdrop sets isPlaying to false; stopPropagation on the inner panel prevents accidental dismissal.

    <AnimatePresence>
      {isPlaying && videoUrl && (
        <motion.div
          initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }}
          className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 p-4"
          onClick={() => setIsPlaying(false)}
        >
          <motion.div
            initial={{ scale: 0.9 }} animate={{ scale: 1 }} exit={{ scale: 0.9 }}
            className="relative w-full max-w-4xl aspect-video"
            onClick={(e) => e.stopPropagation()}
          >
            <iframe src={videoUrl} allow="autoplay; fullscreen" allowFullScreen
              title="Video de presentation" className="w-full h-full rounded-[var(--radius-lg)]" />
          </motion.div>
        </motion.div>
      )}
    </AnimatePresence>
  3. Stagger the stat counters with whileInView

    Map over the stats array and give each motion.div an increasing delay: `0.2 + index * 0.08`. Pass `viewport={{ once: true }}` so the animation does not replay on scroll-up. The accent-colored value sits in a large bold p tag, the label in a muted sm p below it.

    {stats.map((stat, i) => (
      <motion.div
        key={stat.label}
        initial={{ opacity: 0, y: 16 }}
        whileInView={{ opacity: 1, y: 0 }}
        viewport={{ once: true }}
        transition={{ delay: 0.2 + i * 0.08, duration: 0.4 }}
        className="text-center"
      >
        <p className="text-3xl font-bold" style={{ color: "var(--color-accent)" }}>
          {stat.value}
        </p>
        <p className="mt-1 text-sm" style={{ color: "var(--color-foreground-light)" }}>
          {stat.label}
        </p>
      </motion.div>
    ))}

When to use it

Use it on company or product about pages where you have a short explainer or demo video to surface. It works well as a mid-page trust section on landing pages, after the hero, before pricing. Skip it when you have no video asset worth showing, or when the page already has a heavy media section nearby. On very conversion-focused pages the modal interaction can distract; in that case a simple inline video autoplay might serve better.

Used by

  • Loom, Uses a hero video thumbnail with a large play button that opens a product demo in a fullscreen lightbox modal.
  • Notion, Features a centered about/product video section with a thumbnail, play overlay, and key stats beneath the video player.
  • Webflow, Places a product walkthrough video with a play button overlay on its marketing pages, opening into a modal.

FAQ

Why use an iframe instead of a native HTML video element?

An iframe lets you embed hosted videos (YouTube, Vimeo, Wistia) without self-hosting the file. Pass the embed URL with autoplay parameters in the query string. For self-hosted files, swap the iframe for a video element with `autoPlay` and `controls`.

How do I stop the video when the modal closes?

Because the iframe unmounts when `isPlaying` goes false, the browser stops playback automatically. If you switch to a native video element, call `videoRef.current.pause()` inside the close handler before setting state.

Can I use this section without the stats row?

Yes. The stats block is conditionally rendered (`stats && stats.length > 0`), so passing an empty array or omitting the prop removes it entirely with no layout side effects.

Is the modal keyboard-accessible?

The close button is a native button element with an aria-label, so it receives focus and responds to Enter/Space. For full trap-focus behavior, add a focus trap library or manually move focus to the close button on open and restore it to the thumbnail on close.

"use client";

import React, { useState } from "react";
import { motion, AnimatePresence } from "framer-motion";
import { Play, X } from "lucide-react";

interface StatItem {
  value: string;
  label: string;
}

interface AboutVideoProps {
  badge?: string;
  title?: string;
  subtitle?: string;
  thumbnailUrl?: string;
  videoUrl?: string;
  stats?: StatItem[];
}

export default function AboutVideo({
  badge,

Code complet réservé à Pro

Code source intégral, export multi-framework et playground.

Passer en Pro, 9,99€/mois

Reviews

React About Section with Video Modal, Code + Tutorial