Components/c4be898d-aa51-4150-bce5-9d81402a642d

Text Animate

herofrom MagicUI Effects
Sign in to Add to Project
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

PropertyTypeDefaultDescription
bystring
typestring
delaynumber
childrenstring
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"
    }
  }
}