Create dynamic text effects by staggering letters or words with configurable motion and timing, great for hero sections, promotional content, or onboarding screens.
import TextAnimation from "@/registry/text-effects/text-animation/text-animation";
const TextAnimationPreview = () => {
return (
<TextAnimation
className="text-gray-900 dark:text-gray-100 text-xl overflow-hidden"
text="Transform static text into fluid animation"
variant="slideDown"
/>
)
};
export default TextAnimationPreview; Install the following packages before using this component.
import { memo, useMemo } from "react";
import { motion } from "motion/react";
import cn from "@/utils/cn";
export type TextAnimationProps = {
text: string;
variant?:
"fadeIn" |
"slideUp" |
"slideDown" |
"slideLeft" |
"slideRight" |
"zoomIn" |
"zoomOut" |
"blurIn";
unit?: "letter" | "word" | "text";
stagger?: number;
delay?: number;
className?: string;
} & React.ComponentProps<"span">;
const TextAnimation = ({
text = "",
variant = "fadeIn",
unit = "letter",
stagger = 0.01,
delay = 0,
className = "",
...restProps
}: TextAnimationProps) => {
const textUnits = useMemo(() => (
unit === "text" ? (
[text]
) : (
text
.split(" ")
.filter(Boolean)
.map(word => {
if (unit === "word") {
return [word, " "];
} else {
return [
...word
.split("")
.filter(Boolean),
" "
];
}
})
.flat()
)
), [text, unit]);
const animationVariants = {
fadeIn: {
start: {
opacity: 0,
},
end: {
opacity: 1,
},
},
slideUp: {
start: {
opacity: 0,
y: 20,
},
end: {
opacity: 1,
y: 0,
},
},
slideDown: {
start: {
opacity: 0,
y: -20,
},
end: {
opacity: 1,
y: 0,
},
},
slideLeft: {
start: {
opacity: 0,
x: -20,
},
end: {
opacity: 1,
x: 0,
}
},
slideRight: {
start: {
opacity: 0,
x: 20,
},
end: {
opacity: 1,
x: 0,
}
},
zoomIn: {
start: {
scale: 0,
},
end: {
scale: 1,
}
},
zoomOut: {
start: {
opacity: 0,
scale: 1.25,
},
end: {
opacity: 1,
scale: 1,
}
},
blurIn: {
start: {
opacity: 0,
filter: "blur(10px)",
},
end: {
opacity: 1,
filter: "blur(0px)",
}
}
};
return (
<span
{...restProps}
className={cn("overflow-hidden inline-block", className)}
>
{textUnits.map((unit, unitIndex) => (
<motion.span
key={`unit-${unit}-${unitIndex}`}
aria-hidden="true"
className="inline-block"
style={animationVariants[variant].start}
animate={animationVariants[variant].end}
transition={{
type: "tween",
duration: 0.15,
delay: delay + (unitIndex * stagger),
}}
>
{unit === " " ? <> </> : unit}
</motion.span>
))}
<span className="sr-only">
{text}
</span>
</span>
);
};
export default memo(TextAnimation); | Prop | Type | Required | Default | Description |
|---|---|---|---|---|
| text | string | Yes | — | The text content to be animated. |
| variant | "fadeIn" "slideUp" "slideDown" "slideLeft" "slideRight" "zoomIn" "zoomOut" "blurIn" | No | "fadeIn" | Animation style applied to the text. |
| unit | "letter" | "word" | "text" | No | "letter" | Determines how the text is split and animated. |
| stagger | number | No | 0.01 | Time delay between each animated unit (in seconds). |
| delay | number | No | 0 | Delay before the animation starts (in seconds). |
| className | string | No | — | Additional CSS classes for styling. |
If you find this component useful, consider starring the repository on GitHub. Found a bug or have a suggestion? Open an issue to help improve it.