BlurText
react-bits/BlurText-JS-TWFreeComponent
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-JS-TW.jsonSource
1import { motion } from 'motion/react';2import { useEffect, useRef, useState, useMemo } from 'react';34const buildKeyframes = (from, steps) => {5 const keys = new Set([...Object.keys(from), ...steps.flatMap(s => Object.keys(s))]);67 const keyframes = {};8 keys.forEach(k => {9 keyframes[k] = [from[k], ...steps.map(s => s[k])];10 });11 return keyframes;12};1314const BlurText = ({15 text = '',16 delay = 200,17 className = '',18 animateBy = 'words',19 direction = 'top',20 threshold = 0.1,21 rootMargin = '0px',22 animationFrom,23 animationTo,24 easing = t => t,25 onAnimationComplete,26 stepDuration = 0.3527}) => {28 const elements = animateBy === 'words' ? text.split(' ') : text.split('');29 const [inView, setInView] = useState(false);30 const ref = useRef(null);3132 useEffect(() => {33 if (!ref.current) return;34 const observer = new IntersectionObserver(35 ([entry]) => {36 if (entry.isIntersecting) {37 setInView(true);38 observer.unobserve(ref.current);39 }40 },41 { threshold, rootMargin }42 );43 observer.observe(ref.current);44 return () => observer.disconnect();45 }, [threshold, rootMargin]);4647 const defaultFrom = useMemo(48 () =>49 direction === 'top' ? { filter: 'blur(10px)', opacity: 0, y: -50 } : { filter: 'blur(10px)', opacity: 0, y: 50 },50 [direction]51 );5253 const defaultTo = useMemo(54 () => [55 {56 filter: 'blur(5px)',57 opacity: 0.5,58 y: direction === 'top' ? 5 : -559 },60 { filter: 'blur(0px)', opacity: 1, y: 0 }61 ],62 [direction]63 );6465 const fromSnapshot = animationFrom ?? defaultFrom;66 const toSnapshots = animationTo ?? defaultTo;6768 const stepCount = toSnapshots.length + 1;69 const totalDuration = stepDuration * (stepCount - 1);70 const times = Array.from({ length: stepCount }, (_, i) => (stepCount === 1 ? 0 : i / (stepCount - 1)));7172 return (73 <p ref={ref} className={`blur-text ${className} flex flex-wrap`}>74 {elements.map((segment, index) => {75 const animateKeyframes = buildKeyframes(fromSnapshot, toSnapshots);7677 const spanTransition = {78 duration: totalDuration,79 times,80 delay: (index * delay) / 100081 };82 spanTransition.ease = easing;8384 return (85 <motion.span86 className="inline-block will-change-[transform,filter,opacity]"87 key={index}88 initial={fromSnapshot}89 animate={inView ? animateKeyframes : fromSnapshot}90// … 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 |
