Scroll Stagger Text
An animated component that reveals text on scroll.
This is some visible content. Scroll down to reveal some more text content
Lorem ipsum, dolor sit amet consectetur adipisicing elit. Tenetur magni.
Usage
To use the StaggerText
component, follow these steps:
- Installation: Install the necessary package using npm, yarn or pnpm.
npm i framer-motion clsx tailwind-merge
-
Tailwind CSS: Install tailwind css or you can customize the styling according to your ui library
-
Add util file:
lib/utils.ts
import { type ClassValue, clsx } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
- Source code: Copy & paste the below code into
component/ui/motion
or according to your code structure
"use client";
import { cn } from "@/lib/utils";
import {
motion,
MotionValue,
useScroll,
useSpring,
useTransform,
type UseScrollOptions,
} from "framer-motion";
import { PropsWithChildren, useEffect, useRef } from "react";
export interface ScrollStaggerText extends PropsWithChildren {
className?: string;
container?: UseScrollOptions["container"];
}
export function ScrollStaggerText({
className,
children,
}: ScrollStaggerText) {
const paragraphRef = useRef<HTMLParagraphElement>(null);
const { scrollYProgress } = useScroll({
target: paragraphRef,
offset: ["start end", "center center"],
});
useEffect(
() => scrollYProgress.on("change", (v) => console.log(v)),
[scrollYProgress]
);
if (typeof children !== "string") {
throw Error("ScrollStaggerText only accept children of type string");
}
const words = children.split(" ");
return (
<p
ref={paragraphRef}
className={cn(
"flex flex-wrap text-3xl sm:text-4xl md:text-5xl font-bold",
className
)}
>
{words.map((word, i) => (
<Word
key={word + i}
scrollYProgress={scrollYProgress}
range={[i / words.length, (i + 1) / words.length]}
>
{word}
</Word>
))}
</p>
);
}
interface WordProps extends PropsWithChildren {
range: [number, number];
scrollYProgress: MotionValue<number>;
}
function Word({ children, range, scrollYProgress }: WordProps) {
const motionY = useTransform(scrollYProgress, range, [50, 0]);
const y = useSpring(motionY, { stiffness: 600, damping: 80 });
return (
<span className="overflow-hidden">
<motion.span style={{ y }} className="block">
{children}
</motion.span>
</span>
);
}