Letter Pull Up
Letters spring up one by one when the heading comes into view.
components/ui/letter-pull-up.tsx
"use client";
import { motion } from "motion/react";
type LetterPullUpProps = {
text: string;
className?: string;
/** Seconds before the first letter */
delay?: number;
};
export function LetterPullUp({ text, className = "", delay = 0 }: LetterPullUpProps) {
return (
<motion.h2
aria-label={text}
className={className}
initial="hidden"
whileInView="visible"
viewport={{ once: true, amount: 0.6 }}
variants={{ hidden: {}, visible: { transition: { staggerChildren: 0.035, delayChildren: delay } } }}
>
{text.split("").map((char, i) => (
<motion.span
key={i}
aria-hidden
className="inline-block"
variants={{
hidden: { y: "110%", opacity: 0 },
visible: { y: "0%", opacity: 1, transition: { type: "spring", stiffness: 320, damping: 22 } },
}}
>
{char === " " ? " " : char}
</motion.span>
))}
</motion.h2>
);
}
export default function Demo() {
return <LetterPullUp text="Pulled up, letter by letter." className="text-center text-4xl font-semibold tracking-tight text-zinc-900 sm:text-5xl" />;
}
Installation
Install the dependencies
$ npm i motion lucide-reactCopy the source into
components/ui/letter-pull-up.tsxcomponents/ui/letter-pull-up.tsx"use client"; import { motion } from "motion/react"; type LetterPullUpProps = { text: string; className?: string; /** Seconds before the first letter */ delay?: number; }; export function LetterPullUp({ text, className = "", delay = 0 }: LetterPullUpProps) { return ( <motion.h2 aria-label={text} className={className} initial="hidden" whileInView="visible" viewport={{ once: true, amount: 0.6 }} variants={{ hidden: {}, visible: { transition: { staggerChildren: 0.035, delayChildren: delay } } }} > {text.split("").map((char, i) => ( <motion.span key={i} aria-hidden className="inline-block" variants={{ hidden: { y: "110%", opacity: 0 }, visible: { y: "0%", opacity: 1, transition: { type: "spring", stiffness: 320, damping: 22 } }, }} > {char === " " ? " " : char} </motion.span> ))} </motion.h2> ); } export default function Demo() { return <LetterPullUp text="Pulled up, letter by letter." className="text-center text-4xl font-semibold tracking-tight text-zinc-900 sm:text-5xl" />; }Use it
The file exports the component and a
Demoshowing typical usage. Import whichever you need.