Blur In Text

Text emerges from a soft blur into crystal clarity. Perfect for hero headlines and section reveals that demand attention with an elegant, cinematic feel.

Letter by Letter

Each letter emerges from blur individually for a dramatic effect.

Word by Word

Words reveal one at a time for a more readable, impactful entrance.

Scroll Triggered

Animation triggers when the text scrolls into view.

Installation

npm install framer-motion
pnpm install framer-motion
yarn add framer-motion
bun add framer-motion

Copy and paste the following code into your project.

'use client';import { useEffect, useRef, useState } from 'react';import { motion, useInView, Variants } from 'framer-motion';interface BlurInTextProps {  text: string;  className?: string;  blurAmount?: number;  duration?: number;  delay?: number;  stagger?: number;  direction?: 'in' | 'out';  split?: 'letter' | 'word' | 'none';  trigger?: 'mount' | 'inView' | 'hover';  yOffset?: number;}export const BlurInText = ({  text,  className = '',  blurAmount = 10,  duration = 0.8,  delay = 0,  stagger = 0.05,  direction = 'in',  split = 'letter',  trigger = 'mount',  yOffset = 20,}: Readonly<BlurInTextProps>) => {  const containerRef = useRef<HTMLSpanElement>(null);  const isInView = useInView(containerRef, { once: true, amount: 0.5 });  const [isHovering, setIsHovering] = useState(false);  const [hasAnimated, setHasAnimated] = useState(false);  const shouldAnimate = () => {    switch (trigger) {      case 'mount':        return true;      case 'inView':        return isInView;      case 'hover':        return isHovering;      default:        return false;    }  };  useEffect(() => {    if (shouldAnimate() && !hasAnimated && trigger !== 'hover') {      setHasAnimated(true);    }  }, [isInView]);  const containerVariants: Variants = {    hidden: {},    visible: {      transition: {        staggerChildren: split === 'none' ? 0 : stagger,        delayChildren: delay,      },    },  };  const itemVariants: Variants = {    hidden: {      filter: direction === 'in' ? `blur(${blurAmount}px)` : 'blur(0px)',      opacity: direction === 'in' ? 0 : 1,      y: direction === 'in' ? yOffset : 0,    },    visible: {      filter: direction === 'in' ? 'blur(0px)' : `blur(${blurAmount}px)`,      opacity: direction === 'in' ? 1 : 0,      y: direction === 'in' ? 0 : yOffset,      transition: {        duration,        ease: [0.25, 0.46, 0.45, 0.94],      },    },  };  const getElements = () => {    if (split === 'none') {      return [text];    } else if (split === 'word') {      return text.split(' ');    } else {      return text.split('');    }  };  const elements = getElements();  return (    <motion.span      ref={containerRef}      className={`inline-block ${className}`}      variants={containerVariants}      initial="hidden"      animate={shouldAnimate() || hasAnimated ? 'visible' : 'hidden'}      onMouseEnter={() => trigger === 'hover' && setIsHovering(true)}      onMouseLeave={() => trigger === 'hover' && setIsHovering(false)}    >      {elements.map((element, index) => (        <motion.span          key={`${element}-${index}`}          className="inline-block"          variants={itemVariants}          style={{            whiteSpace: element === ' ' ? 'pre' : 'normal',          }}        >          {element}          {split === 'word' && index < elements.length - 1 && '\u00A0'}        </motion.span>      ))}    </motion.span>  );};export default BlurInText;

Props

Prop

Type

Examples

// Dramatic blur with slow reveal
<BlurInText
  text="Welcome"
  blurAmount={20}
  duration={1.5}
  stagger={0.1}
/>

// Quick word reveal
<BlurInText
  text="Fast and Clean"
  split="word"
  duration={0.5}
  stagger={0.2}
/>

// Blur out effect
<BlurInText
  text="Goodbye"
  direction="out"
  trigger="hover"
/>