Skip to main content
ForgeUI leverages Framer Motion to deliver smooth, declarative animations across all components. Every animation is carefully crafted for performance and user experience.

Framer Motion integration

ForgeUI uses motion/react for all animations. Components import motion elements and animate them using:
  • Initial/Animate states - Define start and end states
  • Transitions - Control timing, easing, and delays
  • Layout animations - Automatic animations when layout changes
  • Variants - Reusable animation configurations

Animation patterns

Staggered animations

The TextReveal component demonstrates staggered word reveals using the stagger utility:
import { motion, stagger, useAnimate } from "motion/react";

const [scope, animate] = useAnimate();

useEffect(() => {
  animate(
    "span",
    {
      opacity: 1,
      filter: filter ? "blur(0px)" : "none",
    },
    {
      duration: 0.5,
      delay: stagger(0.2),
      ease: "easeOut",
    },
  );
}, [animate]);
Each word animates sequentially with a 0.2s stagger delay, creating a cascading reveal effect.

Character-by-character animations

The AnimatedForm component shows how to animate individual characters with custom delays:
const nameStaggerDelay = nameAnimationDuration / name.length;

{name.split("").map((char, index) => (
  <motion.span
    key={`name-${index}`}
    className="inline-block font-[350]"
    initial={{ opacity: 0 }}
    animate={{ opacity: 1 }}
    transition={{
      duration: 0.1,
      delay: index * nameStaggerDelay,
      ease: "easeOut",
    }}
  >
    {char === " " ? "\u00A0" : char}
  </motion.span>
))}
Calculate stagger delays dynamically based on content length for consistent timing across different text lengths.

Layout animations

The AnimatedTabs component uses layoutId for smooth morphing between states:
{isActive && (
  <motion.div
    layoutId="active-tab-background"
    className="absolute inset-0 rounded-full bg-primary"
    initial={false}
    transition={{
      type: "spring",
      stiffness: 500,
      damping: 30,
    }}
  />
)}
The layoutId prop automatically animates the background as it moves between tabs.

Spring animations

Spring physics create natural, bouncy animations:
transition={{
  type: "spring",
  stiffness: 500,
  damping: 30,
}}
  • stiffness: How quickly the spring moves (higher = faster)
  • damping: How quickly the spring settles (higher = less bounce)

SVG path animations

The AnimatedCheckmarkCircle component animates SVG stroke paths:
<motion.circle
  cx="10"
  cy="10"
  r="7"
  stroke="#22c55e"
  strokeWidth="2"
  fill="transparent"
  strokeDasharray={circleLength}
  strokeDashoffset={circleLength}
  animate={{ strokeDashoffset: 0 }}
  transition={{
    duration: strokeDuration,
    ease: "easeInOut",
    delay: strokeDelay,
  }}
/>
Animate from full strokeDashoffset to 0 to create a drawing effect.

Timing and easing

Common easing functions

ForgeUI uses these easing functions for different animation types:
Used for entrance animations and reveals. Creates a fast start with gradual deceleration.
transition={{ ease: "easeOut", duration: 0.5 }}

Duration guidelines

  • Micro-interactions: 0.1-0.2s (button hovers, toggles)
  • UI transitions: 0.3-0.5s (tabs, modals, cards)
  • Complex animations: 0.5-1s (multi-step sequences)
  • Background effects: 2s+ (shimmer, gradients)

AnimatePresence

Use AnimatePresence to animate components when they’re removed from the DOM:
import { AnimatePresence, motion } from "motion/react";

<AnimatePresence mode="popLayout">
  <motion.span
    key={index}
    initial={{ opacity: 0, y: 5 }}
    animate={{ opacity: 1, y: 0 }}
    exit={{ opacity: 0, y: -5 }}
    transition={{ duration: 0.4 }}
  >
    {children}
  </motion.span>
</AnimatePresence>
The mode="popLayout" ensures siblings don’t animate when one element exits.

Performance optimization

Use transform and opacity

Animate transform and opacity properties for best performance:
// Good - GPU accelerated
initial={{ opacity: 0, y: 20, scale: 0.95 }}
animate={{ opacity: 1, y: 0, scale: 1 }}

// Avoid - causes layout recalculation
initial={{ height: 0 }}
animate={{ height: 'auto' }}

Disable animations during resize

The theme provider disables transitions on theme change:
<ThemeProvider
  attribute="class"
  defaultTheme="dark"
  enableSystem
  disableTransitionOnChange
>

Will-change for complex animations

For animations that run continuously:
style={{ willChange: 'transform' }}
Use will-change sparingly - it increases memory usage. Only use it for animations that are actively running.

Customizing animations

Override timing

Most components accept timing props:
<TextReveal
  text="Hello World"
  duration={0.8}        // Animation duration per word
  staggerDelay={0.3}    // Delay between words
/>

Custom transitions

Provide custom transition objects:
<motion.div
  animate={{ x: 100 }}
  transition={{
    type: "spring",
    stiffness: 300,
    damping: 20,
    mass: 0.5,
  }}
/>

Disable animations

Set initial={false} to disable entrance animations:
<motion.div
  initial={false}
  animate={{ opacity: 1 }}
/>

Best practices

Check for reduced motion preferences:
const prefersReducedMotion = window.matchMedia(
  '(prefers-reduced-motion: reduce)'
).matches;

<motion.div
  animate={{ y: prefersReducedMotion ? 0 : 20 }}
  transition={{ duration: prefersReducedMotion ? 0 : 0.5 }}
/>
Animations should enhance, not distract. ForgeUI uses:
  • Small movements (5-20px)
  • Short durations (0.1-0.5s)
  • Subtle opacity changes (0-1)
Trigger animations in response to user interactions rather than on mount. This provides better feedback and feels more responsive.
Maintain consistent timing across similar animations. ForgeUI uses:
  • 0.2s for micro-interactions
  • 0.3s for UI transitions
  • 0.5s for complex animations

Real-world examples

Continuous shimmer effect

From TextShimmer component:
initial={{ backgroundPosition: "105% center" }}
animate={{ backgroundPosition: "-5% center" }}
transition={{
  repeat: Number.POSITIVE_INFINITY,
  duration: 2,
  ease: "linear",
  delay: 0,
  repeatDelay: 0,
}}

Pulsing glow effect

From AnimatedOTP component:
initial={{
  opacity: 0,
  scale: 1,
  filter: "blur(0px)",
}}
animate={{
  opacity: [0, 1, 0],
  scale: [0.85, 1.3],
  filter: "blur(2px)",
}}
transition={{
  duration: 0.5,
  ease: "easeInOut",
  delay: 2.25,
}}

Coordinated sequence

From AnimatedForm component showing multiple animations with calculated delays:
const nameAnimationDuration = Math.ceil(name.length / 5);
const passwordAnimationDuration = 2;

// First animation
strokeDelay={0}
fillDelay={nameAnimationDuration + 0.1}
checkmarkDelay={nameAnimationDuration + 0.2}

// Second animation (waits for first)
strokeDelay={nameAnimationDuration + 0.5}
fillDelay={nameAnimationDuration + passwordAnimationDuration + 0.6}
checkmarkDelay={nameAnimationDuration + passwordAnimationDuration + 0.7}

Build docs developers (and LLMs) love