placeholder="blur" on a Next.js Image Silently Does Nothing If You Forget One Thing

●1 ●4 ●57
calendar_today ago • schedule3 min read

placeholder="blur" is one of those props that works perfectly the first time you try it, in a quick example with a locally imported image, and then quietly stops doing anything the moment you swap that example for a real image coming from a CMS, S3, or any other remote source.

The Example That Works, and Why It Works

import heroImage from '@/public/hero.jpg'; // a static, local import

<Image src={heroImage} alt="Hero" placeholder="blur" />

This works because a statically imported image is processed at build time, and Next.js automatically generates a tiny, blurred base64 placeholder from the actual image's real content during that build step. You get a genuine, accurate blur preview with zero extra configuration, because the framework has direct, build-time access to the actual image file.

Where It Silently Stops Working

// A product image coming from your CMS or a database field
<Image src={product.imageUrl} alt={product.name} placeholder="blur" />

product.imageUrl is a remote URL, a string, not a statically imported file. Next.js has no way to generate a blur placeholder from a URL at build time, since the actual image content isn't available until runtime, when the page is actually being requested, not when the project is built. Without a manually provided blurDataURL prop, placeholder="blur" on a remote image source simply has nothing to use, and the image renders without a placeholder at all, exactly as if the prop had never been added.

Why This Doesn't Throw an Error

This is genuinely a silent no-op, not a crash, which is exactly what makes it easy to miss. The prop exists, the code runs, the page renders, the image loads, just without the blur transition anyone added placeholder="blur" specifically to get. Nothing in the console points at the actual gap, since from Next.js's perspective, nothing is actually wrong, you simply didn't provide the one piece of data the feature needs for a remote source.

The Fix: Provide blurDataURL Explicitly for Remote Images

<Image
  src={product.imageUrl}
  alt={product.name}
  placeholder="blur"
  blurDataURL="data:image/jpeg;base64,/9j/4AAQSkZJRg..." // a real, small base64 placeholder
/>

For this to genuinely work, you need an actual base64-encoded placeholder, generated from the real image, not a generic, unrelated blur applied to every image regardless of its actual content. A common approach is generating and storing this placeholder at the time an image is uploaded, saving it alongside the image URL itself in your database, so it's already available whenever the image needs to render, without a separate runtime computation on every single page load.

// A simplified example of generating a blur placeholder at upload time
import { getPlaiceholder } from 'plaiceholder';

async function generateBlurPlaceholder(imageUrl: string) {
  const buffer = await fetch(imageUrl).then((res) => res.arrayBuffer());
  const { base64 } = await getPlaiceholder(Buffer.from(buffer));
  return base64; // store this alongside the image URL in your database
}

Libraries like plaiceholder exist specifically for this, generating a real, accurate blur placeholder from any image buffer, which you can then store once and reuse on every subsequent render, rather than regenerating it on the fly.

A Cheaper Fallback When a Real Placeholder Isn't Worth the Effort

For cases where generating and storing a genuine per-image placeholder is more infrastructure than the project needs, a generic, static color placeholder is still better than silently getting nothing:

<Image
  src={product.imageUrl}
  alt={product.name}
  placeholder="blur"
  blurDataURL="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxIiBoZWlnaHQ9IjEiPjxyZWN0IHdpZHRoPSIxIiBoZWlnaHQ9IjEiIGZpbGw9IiNlNWU3ZWIiLz48L3N2Zz4="
/>

This is a tiny, solid-color SVG, decoded, just a single gray rectangle, which still gives a smoother loading transition than nothing, even though it isn't a real, content-matched blur of the actual image. Not as polished as a genuine per-image placeholder, genuinely better than the silent no-op most remote images end up with by default.

How to Check if This Is Happening on Your Own Site

Search for placeholder="blur" across your codebase and check, for each match, whether the src is a static import or a remote URL. Any remote src without an accompanying blurDataURL prop is this exact gap, the prop is present, and doing nothing.

grep -B2 -A2 'placeholder="blur"' --include="*.tsx" -r .

The Actual Rule

placeholder="blur" only works automatically for statically imported images. Any remote image source needs an explicit blurDataURL, generated from the real image content, or the prop silently does nothing rather than failing loudly. Generating and storing that placeholder once, at upload time, is the more complete fix. A generic, static color placeholder is a reasonable, low-effort fallback when a real one isn't worth the infrastructure for a given project.

I handle this exact pattern, generating and storing real blur placeholders at upload time, across the dashboards and templates I build at pixelanas.com, and I've covered related image and performance patterns on the blog too.


If you're using placeholder="blur" anywhere with a remote image source, check whether blurDataURL is actually present. If it's not, the prop has been doing nothing this whole time. Drop what you find in the comments.

Get the templates: https://pixelanas.gumroad.com


Anas, full-stack Next.js developer building SaaS products and premium templates. X: @ASheikh69751

🔥 Join developers growing publicly
Share your knowledge, build in public, and grow your developer presence with a global community.

More Posts

TypeScript Complexity Has Finally Reached the Point of Total Absurdity

Karol Modelski - Apr 23

I’m a Senior Dev and I’ve Forgotten How to Think Without a Prompt

Karol Modelski - Mar 19

Complete MDX Blog Setup with next-mdx-remote and Next.js 16 (2026 Beginner Guide)

muhammadali - Jul 30

5 Things This Playwright SQL Fixture Does So You Don't Have To

vitalicset - Apr 13

Merancang Backend Bisnis ISP: API Pelanggan, Paket Internet, Invoice, dan Tiket Support

Masbadar - Mar 13
chevron_left
1.2k Points • 62 Badges
Pakistan • pixelanas.com
40Posts
9Comments
6Connections
Full-stack developer building premium Next.js templates & web apps. Specializing in GSAP animations,... Show more

Related Jobs

View all jobs →

Commenters (This Week)

1 comment
1 comment

Contribute meaningful comments to climb the leaderboard and earn badges!