BlurText
react-bits/BlurText-TS-CSSFreeComponent
Text starts blurred then crisply resolves for a soft-focus reveal effect.
Live preview
live · rendered by the registry
Rendered by @react-bits on their own page — this is the component running, not a screenshot of it.Install it
npx shadcn@latest add https://www.reactbits.dev/r/BlurText-TS-CSS.jsonSource
1import { motion, Transition } from 'motion/react';2import { useEffect, useRef, useState, useMemo } from 'react';34type BlurTextProps = {5 text?: string;6 delay?: number;7 className?: string;8 animateBy?: 'words' | 'letters';9 direction?: 'top' | 'bottom';10 threshold?: number;11 rootMargin?: string;12 animationFrom?: Record<string, string | number>;13 animationTo?: Array<Record<string, string | number>>;14 easing?: (t: number) => number;15 onAnimationComplete?: () => void;16 stepDuration?: number;17};1819const buildKeyframes = (20 from: Record<string, string | number>,21 steps: Array<Record<string, string | number>>22): Record<string, Array<string | number>> => {23 const keys = new Set<string>([...Object.keys(from), ...steps.flatMap(s => Object.keys(s))]);2425 const keyframes: Record<string, Array<string | number>> = {};26 keys.forEach(k => {27 keyframes[k] = [from[k], ...steps.map(s => s[k])];28 });29 return keyframes;30};3132const BlurText: React.FC<BlurTextProps> = ({33 text = '',34 delay = 200,35 className = '',36 animateBy = 'words',37 direction = 'top',38 threshold = 0.1,39 rootMargin = '0px',40 animationFrom,41 animationTo,42 easing = (t: number) => t,43 onAnimationComplete,44 stepDuration = 0.3545}) => {46 const elements = animateBy === 'words' ? text.split(' ') : text.split('');47 const [inView, setInView] = useState(false);48 const ref = useRef<HTMLParagraphElement>(null);4950 useEffect(() => {51 if (!ref.current) return;52 const observer = new IntersectionObserver(53 ([entry]) => {54 if (entry.isIntersecting) {55 setInView(true);56 observer.unobserve(ref.current as Element);57 }58 },59 { threshold, rootMargin }60 );61 observer.observe(ref.current);62 return () => observer.disconnect();63 }, [threshold, rootMargin]);6465 const defaultFrom = useMemo(66 () =>67 direction === 'top' ? { filter: 'blur(10px)', opacity: 0, y: -50 } : { filter: 'blur(10px)', opacity: 0, y: 50 },68 [direction]69 );7071 const defaultTo = useMemo(72 () => [73 {74 filter: 'blur(5px)',75 opacity: 0.5,76 y: direction === 'top' ? 5 : -577 },78 { filter: 'blur(0px)', opacity: 1, y: 0 }79 ],80 [direction]81 );8283 const fromSnapshot = animationFrom ?? defaultFrom;84 const toSnapshots = animationTo ?? defaultTo;8586 const stepCount = toSnapshots.length + 1;87 const totalDuration = stepDuration * (stepCount - 1);88// … truncated
What it pulls in
npm packages
Files it writes
More from @react-bits
All 556 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| AnimatedContentAnimatedContent-JS-CSS | @react-bits | Components | Free | 1 dep | |
| AnimatedContentAnimatedContent-JS-TW | @react-bits | Components | Free | 1 dep | |
| AnimatedContentAnimatedContent-TS-CSS | @react-bits | Components | Free | 1 dep | |
| AnimatedContentAnimatedContent-TS-TW | @react-bits | Components | Free | 1 dep | |
| AnimatedListAnimatedList-JS-CSS | @react-bits | Components | Free | 1 dep · 2 files | |
| AnimatedListAnimatedList-JS-TW | @react-bits | Components | Free | 1 dep | |
| AnimatedListAnimatedList-TS-CSS | @react-bits | Components | Free | 1 dep · 2 files | |
| AnimatedListAnimatedList-TS-TW | @react-bits | Components | Free | 1 dep |
