Headline that resolves word by word from a soft blur, fading and lifting each word into focus with a configurable stagger and initial blur radius.
Preview
Customize
5 props0.6
0.08
12
Installation
Copies the component source and its CSS Module into your project.
npx motionfoundry add blur-in-textWrites components/ui/blur-in-text.tsx and components/styles/blur-in-text.module.css.
Usage
Updates live as you change the controls — only props that differ from the defaults are included.
example.tsx
1import { BlurInText } from "@motionfoundry/ui";2 3export function Example() {4 return (5 <BlurInText />6 );7}Props
Accessibility
Renders a semantic h1/h2/h3/p carrying the full sentence in aria-label while the per-word spans are aria-hidden. Under prefers-reduced-motion words render in their final, unblurred state with no animation.
Toggle prefers-reduced-motion in your OS or browser devtools (Rendering → Emulate CSS media feature) to verify the reduced-motion state.
Source
components/ui/blur-in-text.tsx
1"use client";2 3import * as React from "react";4import { motion, useReducedMotion } from "motion/react";5import styles from "../styles/blur-in-text.module.css";6 7export interface BlurInTextProps {8 /** Text to reveal word by word */9 text?: string;10 /** Per-word animation duration in seconds */11 duration?: number;12 /** Delay between consecutive words in seconds */13 stagger?: number;14 /** Initial blur radius in pixels */15 blur?: number;16 /** Semantic element to render */17 as?: "h1" | "h2" | "h3" | "p";18 /** Optional className */19 className?: string;20}21 22export function BlurInText({23 text = "Motion that feels considered, not decorated.",24 duration = 0.6,25 stagger = 0.08,26 blur = 12,27 as = "h2",28 className = "",29}: BlurInTextProps) {30 const prefersReducedMotion = useReducedMotion();31 const words = text.split(" ").filter(Boolean);32 const Tag = as;33 34 return (35 <Tag aria-label={text} className={`${styles.root} ${className}`.trim()}>36 {words.map((word, i) => (37 <motion.span38 key={`${word}-${i}`}39 aria-hidden="true"40 className={styles.word}41 initial={prefersReducedMotion ? false : { opacity: 0, filter: `blur(${blur}px)`, y: 8 }}42 animate={{ opacity: 1, filter: "blur(0px)", y: 0 }}43 transition={{ duration, delay: i * stagger, ease: "easeOut" }}44 >45 {word}46 {i < words.length - 1 ? " " : null}47 </motion.span>48 ))}49 </Tag>50 );51}