Text Animate
herofrom MagicUI Effects
Text Animate
AI Usage Rules
Use TextAnimate for hero headlines and key text. Requires motion/react (Framer Motion). Choose animation type based on context: blurIn for dramatic reveals, slideUp for clean entrances, scaleUp for impact. Use by="word" for headlines, by="character" for short labels.
Code Template
"use client"
import { cn } from "@/lib/utils"
import { AnimatePresence, motion, type Variants } from "motion/react"
import { useMemo } from "react"
type AnimationType = "fadeIn" | "blurIn" | "slideUp" | "slideDown" | "slideLeft" | "slideRight" | "scaleUp" | "scaleDown"
type ByType = "character" | "word" | "line"
interface TextAnimateProps extends React.ComponentProps<"span"> {
children: string
type?: AnimationType
by?: ByType
delay?: number
duration?: number
startOnView?: boolean
}
const staggerTimings: Record<ByType, number> = { character: 0.03, word: 0.05, line: 0.1 }
const defaultContainerVariants: Variants = {
hidden: { opacity: 0 },
show: (delay: number) => ({ opacity: 1, transition: { staggerChildren: delay } }),
exit: { opacity: 0, transition: { staggerChildren: 0.01, staggerDirection: -1 } },
}
const defaultItemVariants: Record<AnimationType, Variants> = {
fadeIn: { hidden: { opacity: 0 }, show: { opacity: 1 }, exit: { opacity: 0 } },
blurIn: { hidden: { opacity: 0, filter: "blur(10px)" }, show: { opacity: 1, filter: "blur(0px)" }, exit: { opacity: 0, filter: "blur(10px)" } },
slideUp: { hidden: { opacity: 0, y: 20 }, show: { opacity: 1, y: 0 }, exit: { opacity: 0, y: 20 } },
slideDown: { hidden: { opacity: 0, y: -20 }, show: { opacity: 1, y: 0 }, exit: { opacity: 0, y: -20 } },
slideLeft: { hidden: { opacity: 0, x: 20 }, show: { opacity: 1, x: 0 }, exit: { opacity: 0, x: 20 } },
slideRight: { hidden: { opacity: 0, x: -20 }, show: { opacity: 1, x: 0 }, exit: { opacity: 0, x: -20 } },
scaleUp: { hidden: { opacity: 0, scale: 0.5 }, show: { opacity: 1, scale: 1 }, exit: { opacity: 0, scale: 0.5 } },
scaleDown: { hidden: { opacity: 0, scale: 1.5 }, show: { opacity: 1, scale: 1 }, exit: { opacity: 0, scale: 1.5 } },
}
export function TextAnimate({ children, type = "fadeIn", by = "word", delay = 0, duration, startOnView = true, className, ...props }: TextAnimateProps) {
const stagger = staggerTimings[by]
const segments = useMemo(() => {
if (by === "line") return children.split("\n")
if (by === "word") return children.split(/( )/)
return [...children]
}, [children, by])
return (
<AnimatePresence mode="popLayout">
<motion.span
variants={defaultContainerVariants}
initial="hidden"
animate="show"
exit="exit"
custom={stagger}
className={cn("inline-flex flex-wrap", className)}
{...(startOnView ? { whileInView: "show", viewport: { once: true } } : {})}
{...props}
>
{segments.map((segment, i) => (
<motion.span key={i} variants={{ ...defaultItemVariants[type], show: { ...defaultItemVariants[type].show, transition: { duration: duration ?? 0.3 } } }} className={cn(by === "line" && "block", by === "character" && segment === " " && "w-[0.25em]")}>
{segment}
</motion.span>
))}
</motion.span>
</AnimatePresence>
)
}Props Schema
| Property | Type | Default | Description |
|---|---|---|---|
| by | string | — | |
| type | string | — | |
| delay | number | — | |
| children | string | — |
View raw JSON schema
{
"type": "object",
"properties": {
"by": {
"enum": [
"character",
"word",
"line"
],
"type": "string"
},
"type": {
"enum": [
"fadeIn",
"blurIn",
"slideUp",
"slideDown",
"slideLeft",
"slideRight",
"scaleUp",
"scaleDown"
],
"type": "string"
},
"delay": {
"type": "number"
},
"children": {
"type": "string"
}
}
}