Text Reveal On scroll

The TextReveal component creates a smooth, scroll-driven text reveal effect using Framer Motion and Lenis for enhanced scrolling performance. As the user scrolls down the page, the text gradually reveals from left to right using a dynamic CSS mask powered by motion values. A glowing vertical progress bar moves alongside the reveal, visually indicating scroll progression.

lenisframer-motion

HELLO

HELLO

Installation

Install the required dependencies:

npm install lenis framer-motion
components/ui/text-reveal-on-scroll.tsx
"use client";
import {
  useScroll,
  motion,
  useTransform,
  useMotionTemplate,
} from "framer-motion";
import Lenis from "lenis";
import { useEffect, useRef } from "react";

type CardProps = {
  text?: string;
};

export default function TextReveal({ text }: CardProps) {
  useEffect(() => {
    const lenis = new Lenis();

    function raf(time: any) {
      lenis.raf(time);
      requestAnimationFrame(raf);
    }

    requestAnimationFrame(raf);
  }, []);
  const containerRef = useRef(null);

  const { scrollYProgress } = useScroll({
    target: containerRef,
    offset: ["start start", "end end"],
  });

  // Reveal progress (0 → 100%)
  const reveal = useTransform(scrollYProgress, [0, 1], [100, 0]);

  // Smooth gradient mask
  const mask = useMotionTemplate`
    linear-gradient(
      to right,
      black 0%,
      black ${reveal}%,
      transparent calc(${reveal}% + 0%)
    )
  `;

  const barPosition = useTransform(scrollYProgress, [0, 1], ["100%", "0%"]);

  return (
    <div ref={containerRef} className="relative h-[50vh] w-full cursor-default">
      <div className="sticky top-0 h-full flex items-center justify-center">
        <div className="relative">
          <p className="absolute top-0 left-0 text-[#9c8e8e] uppercase font-bold text-[8vw] opacity-10 leading-[7.5vw] m-0 z-[1]">
            {text || "HELLO"}
          </p>

          <motion.div
            style={{
              WebkitMaskImage: mask,
              maskImage: mask,
            }}
            className="relative z-[2]"
          >
            <p className="text-[#b7ab98] uppercase font-bold text-[8vw] leading-[7.5vw] m-0">
              {text || "HELLO"}
            </p>
          </motion.div>

          <motion.div
            style={{ left: barPosition }}
            className="absolute top-0 h-full w-[3px] bg-[#b7ab98] z-[3] shadow-[0_0_20px_#b7ab98]"
          />
        </div>
      </div>
    </div>
  );
}

Usecase

HELLO

HELLO

* only center rotate