Image Effects
BlurEntrance
The picture waits blurred and slightly enlarged, then settles into sharp focus once it is loaded and on screen.
Download
- React
- Tailwind CSS v4
- motion
components/ui/blur-entrance.tsx
"use client";
import { useEffect, useRef, useState } from "react";
import { motion, useInView, useReducedMotion } from "motion/react";
type BlurEntranceProps = {
src: string;
alt: string;
/** Blur at the start, in pixels. */
blur?: number;
/** Seconds the picture takes to come into focus. */
duration?: number;
/** Seconds to wait first. Use it to stagger a row. */
delay?: number;
/** Play once, or again each time it comes back on screen. */
once?: boolean;
/** Set the size here. */
className?: string;
};
/* BlurEntrance: the picture waits blurred and slightly enlarged, then settles into sharp focus once it is loaded and on screen. */
export function BlurEntrance({ src, alt, blur = 24, duration = 1.2, delay = 0, once = true, className = "aspect-video" }: BlurEntranceProps) {
const reduce = useReducedMotion();
const box = useRef<HTMLDivElement>(null);
const picture = useRef<HTMLImageElement>(null);
const seen = useInView(box, { once, amount: 0.3 });
const [ready, setReady] = useState(false);
useEffect(() => {
let live = true;
const done = () => {
if (live) setReady(true);
};
picture.current?.decode().then(done, done);
return () => {
live = false;
};
}, [src]);
const hidden = { filter: `blur(${blur}px)`, scale: 1.12, opacity: 0.4 };
const sharp = reduce || (seen && ready);
return (
<div ref={box} className={`relative overflow-hidden ${className}`}>
<motion.img
ref={picture}
src={src}
alt={alt}
className="absolute inset-0 size-full object-cover"
initial={hidden}
animate={sharp ? { filter: "blur(0px)", scale: 1, opacity: 1 } : hidden}
transition={reduce ? { duration: 0 } : sharp ? { duration, delay, ease: [0.16, 1, 0.3, 1] } : { duration: 0.3 }}
/>
</div>
);
}
1. Install the dependency.
npm i motion2. Paste the Code tab into
components/ui/blur-entrance.tsx. The styles are Tailwind CSS v4 classes.3. Use it. This is the preview above:
app/blur-entrance-demo.tsx
"use client"; import { useState } from "react"; import { BlurEntrance } from "@/components/ui/blur-entrance"; export default function BlurEntranceExample() { const [run, setRun] = useState(0); return ( <div className="flex h-full flex-col items-center justify-center gap-3 bg-[#0b0a14] p-4"> <BlurEntrance key={run} src="/demo/moss.jpg" alt="Green hills in soft light" className="aspect-video w-full max-w-[460px] rounded-2xl" /> <button type="button" onClick={() => setRun(run + 1)} className="rounded-full bg-white/10 px-4 py-1.5 text-sm text-white hover:bg-white/20"> Play again </button> </div> ); }
Props
| Name | Type | Default | What it does |
|---|---|---|---|
| src | string | — | Picture address. |
| alt | string | — | Text for screen readers. |
| blur | number | 24 | Blur at the start, in pixels. |
| duration | number | 1.2 | Seconds the picture takes to come into focus. |
| delay | number | 0 | Seconds to wait first. Use it to stagger a row. |
| once | boolean | true | Play once, or again each time it comes back on screen. |
| className | string | "aspect-video" | Set the size here. |