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

PropTypeDefault
text?
string
required
blurAmount?
number
10
duration?
number
0.8
delay?
number
0
stagger?
number
0.05
direction?
'in' | 'out'
'in'
split?
'letter' | 'word' | 'none'
'letter'
trigger?
'mount' | 'inView' | 'hover'
'mount'
yOffset?
number
20
className?
string
''

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"
/>