CoreAdvanced Text Animationsmotion/react

KineticType

Expressive kinetic typography engine where words and characters animate with coordinated rotation, scale, optical blur, and 3D position trajectories across four presets.

Preview

Customize

7 props
0.055
0.65
14
8

Installation

Copies the component source and its CSS Module into your project.

npx motionfoundry add kinetic-type

Writes components/ui/kinetic-type.tsx and components/styles/kinetic-type.module.css.

Usage

Updates live as you change the controls — only props that differ from the defaults are included.

example.tsx
1import { KineticType } from "@motionfoundry/ui";2 3export function Example() {4  return (5    <KineticType />6  );7}

Props

PropTypeDefault
text

Headline or display copy to animate.

string'Choreographed kinetic...'
preset

Coordinated motion trajectory preset.

'elastic' | 'wave' | 'orbit' | 'snap''elastic'
splitBy

Tokenization granularity.

'words' | 'chars''words'
stagger

Delay in seconds between consecutive tokens.

number0.055
duration

Animation duration per token in seconds.

number0.65
rotationIntensity

Starting rotation angle in degrees.

number14
blurStrength

Initial Gaussian blur radius in pixels.

number8

Accessibility

Exposes the full unsplit sentence via aria-label on the semantic heading while marking animated glyph wrappers aria-hidden='true'. Renders static text immediately when prefers-reduced-motion is active.

Toggle prefers-reduced-motion in your OS or browser devtools (Rendering → Emulate CSS media feature) to verify the reduced-motion state.

Source

components/ui/kinetic-type.tsx
1"use client";2 3import * as React from "react";4import { motion, useReducedMotion } from "motion/react";5import styles from "../styles/kinetic-type.module.css";6 7export interface KineticTypeProps {8  /** Text string to animate */9  text: string;10  /** Coordinated motion preset */11  preset?: "elastic" | "wave" | "orbit" | "snap";12  /** Split granularity */13  splitBy?: "words" | "chars";14  /** Stagger interval in seconds */15  stagger?: number;16  /** Duration per token in seconds */17  duration?: number;18  /** Initial rotation angle in degrees */19  rotationIntensity?: number;20  /** Initial Gaussian blur in pixels */21  blurStrength?: number;22  /** Optional className */23  className?: string;24}25 26export function KineticType({27  text,28  preset = "elastic",29  splitBy = "words",30  stagger = 0.055,31  duration = 0.65,32  rotationIntensity = 14,33  blurStrength = 8,34  className = "",35}: KineticTypeProps) {36  const prefersReducedMotion = useReducedMotion();37  const tokens = React.useMemo(38    () => (splitBy === "chars" ? Array.from(text) : text.split(" ")),39    [text, splitBy]40  );41 42  if (prefersReducedMotion) {43    return <h2 className={`${styles.root} ${className}`.trim()}>{text}</h2>;44  }45 46  const getInitialState = (index: number) => {47    const sign = index % 2 === 0 ? 1 : -1;48    switch (preset) {49      case "wave":50        return {51          opacity: 0,52          y: 26 * sign,53          rotate: rotationIntensity * sign,54          scale: 0.86,55          filter: `blur(${blurStrength}px)`,56        };57      case "orbit":58        return {59          opacity: 0,60          x: -18 * sign,61          y: 22,62          rotate: rotationIntensity * 1.4,63          scale: 0.72,64          filter: `blur(${blurStrength}px)`,65        };66      case "snap":67        return {68          opacity: 0,69          y: 34,70          rotate: -rotationIntensity * 0.5,71          scale: 1.18,72          filter: `blur(${blurStrength}px)`,73        };74      case "elastic":75      default:76        return {77          opacity: 0,78          y: 28,79          rotate: rotationIntensity,80          scale: 0.8,81          filter: `blur(${blurStrength}px)`,82        };83    }84  };85 86  return (87    <h2 aria-label={text} className={`${styles.root} ${className}`.trim()}>88      <span aria-hidden="true" className={styles.stage}>89        {tokens.map((token, idx) => (90          <motion.span91            key={`${token}-${idx}`}92            initial={getInitialState(idx)}93            animate={{94              opacity: 1,95              x: 0,96              y: 0,97              rotate: 0,98              scale: 1,99              filter: "blur(0px)",100            }}101            transition={102              preset === "elastic"103                ? {104                    type: "spring",105                    stiffness: 260,106                    damping: 18,107                    delay: idx * stagger,108                  }109                : {110                    duration,111                    delay: idx * stagger,112                    ease: [0.16, 1, 0.3, 1],113                  }114            }115            className={styles.token}116          >117            {token === " " ? "\u00A0" : token}118            {splitBy === "words" && idx < tokens.length - 1 ? "\u00A0" : ""}119          </motion.span>120        ))}121      </span>122    </h2>123  );124}