Building a Smart BlurImage Component in Next.js 15 App Router


Never miss an update
Subscribe to receive news and special offers.
By subscribing you agree to our Privacy Policy.
Stop shipping blurry or broken images in Next.js
We've all been there:
In this quick guide, i'll share a dead-simple reusable BlurImage component i've been using in all my Next.js 14+ App Router projects. It handles:
✅ Blur-up placeholder while loading
✅ Fallback avatar if the image fails
✅ Fully typed, drop-in for any <Image /> component
When dealing with dynamic content (user profiles, external images, CMS sources), you can’t always trust the image URL.
Sometimes:
The native <Image /> component in Next.js is great for optimization — but you still need a tiny UX layer on top.
Let’s build a very simple wrapper around Next.js <Image /> using only App Router compatible code:
"use client";
import { useEffect, useState } from "react";
import Image, { ImageProps } from "next/image";
import { cn } from "@/lib/utils";
export default function BlurImage(props: ImageProps) {
const [loading, setLoading] = useState(true);
const [src, setSrc] = useState(props.src);
useEffect(() => setSrc(props.src), [props.src]);
return (
<Image
{...props}
src={src}
alt={props.alt}
className={cn(
"rounded-lg object-cover object-center",
props.className,
loading ? "blur-[2px]" : "blur-0"
)}
onLoad={() => setLoading(false)}
onError={() => {
setSrc(`https://avatar.vercel.sh/${props.alt}`);
}}
/>
);
}"Note: cn() here is a utility to combine Tailwind classes — feel free to replace it with your own className merge function."
Good question.
Next.js built-in blur placeholders work if you provide the blurDataURL. But for dynamic external URLs, you may not always have one available. This component gives you a consistent experience even when you can't precompute placeholders.
Let’s say you’re rendering user avatars:
import BlurImage from "@/components/ui-components/BlurImage";
export function ProfilePage() {
return (
<main>
<div className="flex items-center gap-4">
<BlurImage
src="/profile-pic.jpg"
alt="User profile"
width={100}
height={100}
className="border-2 border-gray-200"
/>
<div>
<h1>John Doe</h1>
<p>Software Engineer</p>
</div>
</div>
</main>
);
}✅ If user.profileImageUrl is valid: clean, sharp image after load ✅ If it fails: fallback avatar using user name ✅ Always smooth: initial blur transition
Sometimes it's these tiny components that:
If you found this useful, follow the blog or join my newsletter for more practical Next.js patterns like this one. Also — feel free to share this with your dev friends who keep shipping broken images 😉