next.js
next.js copied to clipboard
NextJS 13 next/image blur placeholder not working
Verify canary release
- [X] I verified that the issue exists in the latest Next.js canary release
Provide environment information
Operating System: Platform: win32 Arch: x64 Version: Windows 10 Home Single Language Binaries: Node: 16.13.2 npm: N/A Yarn: N/A pnpm: N/A Relevant packages: next: 13.0.0 eslint-config-next: 13.0.0 react: 18.2.0 react-dom: 18.2.0
What browser are you using? (if relevant)
No response
How are you deploying your application? (if relevant)
vercel
Describe the Bug
next/image blur placeholder not working in both development and production. Demo: https://nextjs-13-image.vercel.app/ It's working on NextJS 12
Expected Behavior
Image Blur when images are loading
Link to reproduction
https://github.com/imPrathamDev/nextjs-13-image
To Reproduce
https://github.com/imPrathamDev/nextjs-13-image
It also doesn't work on the example from the docs. I first thought maybe something changed but the example doesn't work either.
@ShockTr At least for me, the shimmer effect does seem to be working in the example you've linked. Try opening the link in an incognito window and refreshing the page - you'll notice a grayish-black shimmer show up for a split second before getting replaced by the image.
@ShockTr At least for me, the shimmer effect does seem to be working in the example you've linked. Try opening the link in an incognito window and refreshing the page - you'll notice a grayish-black shimmer show up for a split second before getting replaced by the image.
I can still reproduce my problem.
My browser info if it can be helpful: Google Chrome 106.0.5249.119 (64 bit) Windows 11(22623.746)
@ShockTr the video you've shared - that is exactly how the component is supposed to behave with the 'shimmer' effect. If you look at the code, you will see that the custom svg-based shimmer component is passed into the blurDataUrl field for the Image component. That's why you see the grayish-black shimmer before the actual mountains image loads in.
For comparison, check out the 'placeholder' example here. For this one, the blur part is actually taken from the image. In the code for this one, you will notice no blurDataUrl property - that is because the image src is locally imported beforehand.
Note - the blur behavior is different in the nextjs v13 next/image component from what it was in the v12 and older next/image components. You shouldn't expect the same behavior from the v13 next/image. I'm writing another explanation to address the issue faced by @imPrathamDev - that might help explain this difference better.
@imPrathamDev the v13 next/image component handles the blurring effect differently as compared to the <= v12 next/image component. You can do either one of these two things:
- generate a placeholder using a tool like Plaiceholder and pass that in as the blurDataURI
- instead of passing the entire >1 Mb size image to the blurDataURI (which would take as long as the original image to load, and hence defeat the purpose of having a placeholder), pass in a compressed (smaller dimension + lower quality = smaller file size) version of the same image
Both of the strategies above should get the placeholder="blur" effect working for you. Let me know if they don't.
FYI, in v12 (and earlier), the component directly sets the background image equal to the blurDataUrl along with applying a blur filter to achieve the effect:

In v13, the component sets an actual data uri by loading the blurDataUri via an svg

@imPrathamDev @ishaqibrahimbot my app on Next 13 loads smaller images (1kb in size - 10px:10px) and the blurred image placeholders are gone. It previously worked on Next 12.
@alextenczar can you share the code for your app (or if it's big/private, create a small code sandbox that reproduces the issue)? I'll try to look into why the blurred image placeholders are gone.
@ishaqibrahimbot
Here is the code from inside of my return function of the page's TSX.
{returnData.map((value: any, index: any) => {
let imgUrl = value.artworkUrl100.replace('100x100', '300x300')
let blurImgUrl = value.artworkUrl100.replace('100x100', '10x10')
let showId = value.id
let imagePriority = false;
if(index <= 20) {
imagePriority = true;
}
return (
<div className={styles.showItem} key={value.id}>
<Link href={{ pathname: `/show`, query: { id: showId } }}>
<Image className={styles.thumb} src={imgUrl} alt={value.name} width={300} height={300} priority={imagePriority} placeholder="blur" blurDataURL={blurImgUrl}/>
<p>{value.name}</p>
<p>{value.artistName}</p>
</Link>
</div>
);
})
}
Basically, I replace the file's URL to the one used for 10x10 images. Then feed it as the blurDataURL. These file URLs are contained within a prop I receive during getServerSideProps. And just a note: importing next/legacy/image brings back the expected blur.
Link to code's file: https://github.com/alextenczar/ApplePodcastsWeb/blob/dev/pages/locale/%5Bid%5D/index.tsx
Same issue here. I just upgraded next to 13.0.1 and the blur is not working. In my case, the blurDataURL displays the broken image icon. Was working fine before the upgrade.
@imPrathamDev @alextenczar @Panix-dev so here's what I've understood after some digging around.
The way you're expected to use the blurDataUrl property on the Next/Image component has changed with nextjs v13. In earlier versions, you could pass a url to an image and that would be sufficient to achieve the blur effect.
However, with the new v13 Next/Image component, you're expected to pass base64-encoded image data to the blurDataUrl property. Passing in a simple url like before no longer works cause the url is a reference to where the image is stored, whereas blurDataUrl requires actual image data. This much is already made clear in the beta docs for the new image component:

So now, if you want to achieve the blur effect, the best strategy is to use a tool like Plaiceholder to get a suitable base64 placeholder image on the server side, and pass that into the blurDataUrl property.
Here's what this would look like for the nextjs-13-image app @imPrathamDev has shared:
import Image from "next/image";
import { getPlaiceholder } from "plaiceholder";
export default async function Home() {
const imageUrls = [
"https://firebasestorage.googleapis.com/v0/b/replay-chat-dd920.appspot.com/o/next-images%2Fbilly-huynh-W8KTS-mhFUE-unsplash.jpg?alt=media&token=c754dcd9-5bb6-422b-bba2-35b40f1b047f",
"https://firebasestorage.googleapis.com/v0/b/replay-chat-dd920.appspot.com/o/next-images%2Fmilad-fakurian-nY14Fs8pxT8-unsplash.jpg?alt=media&token=88ac6424-3b03-4da2-8099-2768d11cce35",
"https://firebasestorage.googleapis.com/v0/b/replay-chat-dd920.appspot.com/o/next-images%2Fpawel-czerwinski-vI5XwPbGvmY-unsplash.jpg?alt=media&token=bc94569f-c146-44d3-9e95-4334783e77c1",
"https://firebasestorage.googleapis.com/v0/b/replay-chat-dd920.appspot.com/o/next-images%2Fpawel-czerwinski-prMn9KINLtI-unsplash.jpg?alt=media&token=e437b711-3518-4fbe-a6de-ab6950908277",
];
const placeholders = await Promise.all(
imageUrls.map(async (url) => {
const { base64 } = await getPlaiceholder(url);
return base64;
})
);
return (
<div>
{imageUrls.map((url, index) => {
return (
<Image
key={url}
src={url}
alt="Vercel Logo"
blurDataURL={placeholders[index]}
placeholder="blur"
width={420}
height={220}
/>
);
})}
</div>
);
}
Result:
https://user-images.githubusercontent.com/74908398/199297214-03bdb3c3-9df4-485f-95e0-e353b9b8c2f2.mov
This example uses React Server Components (which you can use inside the new app directory) so if your app is using the pages directory, you will have to generate the placeholders in either of getStaticProps or getServerSideProps or inside an API route (Plaiceholder works in a nodejs environment, not in the browser).
Let me know if you guys have any questions about this explanation.
@ishaqibrahimbot Thank you for this. I tried a variation of your solution but have reverted to using Next's Legacy Image component. While it worked, implementing plaiceholder significantly slowed down my site (maybe because I'm running it with getserversideprops?). If you're curious what I did, free look at my code and see if anything could be improved. Any help would be appreciated, thank you!
https://github.com/alextenczar/ApplePodcastsWeb/blob/55e690fc31fd41195fd2b02deb6080f6cf28059a/pages/locale/%5Bid%5D/index.tsx
@ishaqibrahimbot Thank you for this. I tried a variation of your solution but have reverted to using Next's Legacy Image component. While it worked, implementing plaiceholder significantly slowed down my site (maybe because I'm running it with getserversideprops?). If you're curious what I did, free look at my code and see if anything could be improved. Any help would be appreciated, thank you!
https://github.com/alextenczar/ApplePodcastsWeb/blob/55e690fc31fd41195fd2b02deb6080f6cf28059a/pages/locale/%5Bid%5D/index.tsx
@alextenczar Try to deploy on vercel then check speed because sometimes next app works slow in development but in production works fine.
@alextenczar is there a reason you're using getServerSideProps and not doing SSG with getStaticProps? Your use case seems to suggest that you'd be better off generating your pages at build time and then setting a suitable revalidate time to make sure the data does not become stale. I've modified your file to use getStaticProps and it's working a whole lot faster. Here's the code:
import type {
GetStaticPropsContext,
InferGetStaticPropsType,
NextPage,
} from 'next'
import styles from '../../../styles/Home.module.scss'
import Link from 'next/link'
import Layout from '../../../components/layout'
import CountrySelect from '../../../components/countrySelect'
import Image from 'next/image'
import { getPlaiceholder } from 'plaiceholder'
export async function getStaticPaths({}) {
let countriesArr = [
'dz',
'ao',
'ai',
'ag',
.
.
.
'vn',
'ye',
'zm',
'zw',
]
return {
paths: countriesArr
.slice(0, 5)
.concat(['us'])
.map((countryCode) => `/locale/${countryCode}`), // limiting the number of pages built for now
fallback: false,
}
}
async function genPlaceholders(item: any) {
const placeholders = await Promise.all(
item.map((index: any) => {
return getPlaiceholder(index.artworkUrl100.replace('100x100', '10x10'))
})
)
return placeholders
}
export async function getStaticProps({
params,
}: GetStaticPropsContext<{ id: string }>) {
// Fetch data from external API
const id = params?.id || 'us'
const res = await fetch(
`https://rss.applemarketingtools.com/api/v2/${id}/podcasts/top/50/podcasts.json`
)
const data = await res.json()
// 404 if no results retrieved
if (!data?.feed?.results)
return {
notFound: true,
}
const placeholders = await genPlaceholders(data.feed.results)
let returnPlaceholders: any = []
placeholders.map((index) => {
returnPlaceholders.push(index.base64)
})
//console.log(test)
// Pass data to the page via props
let returnData: Array<object>
returnData = data.feed.results
return {
props: { returnData, countryCode: id, returnPlaceholders },
revalidate: 60 * 60, // revalidate cache every hour
}
}
interface Props {
returnData: Array<object>
countryCode?: String
returnPlaceholders: any
}
function LocaleHome(props: InferGetStaticPropsType<typeof getStaticProps>) {
let { returnData, countryCode, returnPlaceholders } = props
return (
<Layout>
<div className={styles.headerSelect}>
<h2>Top Shows In:</h2>
<CountrySelect country={{ countryCode }}></CountrySelect>
</div>
<div className={styles.showContainer}>
{returnData.map((value: any, index: any) => {
let imgUrl = value.artworkUrl100.replace('100x100', '300x300')
let blurImgUrl = value.artworkUrl100.replace('100x100', '10x10')
let showId = value.id
let imagePriority = false
if (index <= 20) {
imagePriority = true
}
return (
<div className={styles.showItem} key={value.id}>
<Link href={{ pathname: `/show`, query: { id: showId } }}>
<Image
className={styles.thumb}
src={imgUrl}
alt={value.name}
width={300}
height={300}
priority={imagePriority}
placeholder="blur"
blurDataURL={returnPlaceholders[index]}
/>
<p>{value.name}</p>
<p>{value.artistName}</p>
</Link>
</div>
)
})}
</div>
</Layout>
)
}
export default LocaleHome
Thank you @imPrathamDev I'll look into my options for re-validation. I still think it's a shame that placeholders don't play well with getserversideprops when several image placeholders need to get generated. The previous method of generating placeholders was sufficient for my needs. Maybe a solution for this use-case will pop up later on.
@imPrathamDev @alextenczar @Panix-dev so here's what I've understood after some digging around.
The way you're expected to use the blurDataUrl property on the Next/Image component has changed with nextjs v13. In earlier versions, you could pass a url to an image and that would be sufficient to achieve the blur effect.
However, with the new v13 Next/Image component, you're expected to pass base64-encoded image data to the blurDataUrl property. Passing in a simple url like before no longer works cause the url is a reference to where the image is stored, whereas blurDataUrl requires actual image data. This much is already made clear in the beta docs for the new image component:
![]()
So now, if you want to achieve the blur effect, the best strategy is to use a tool like Plaiceholder to get a suitable base64 placeholder image on the server side, and pass that into the blurDataUrl property.
Here's what this would look like for the nextjs-13-image app @imPrathamDev has shared:
import Image from "next/image"; import { getPlaiceholder } from "plaiceholder"; export default async function Home() { const imageUrls = [ "https://firebasestorage.googleapis.com/v0/b/replay-chat-dd920.appspot.com/o/next-images%2Fbilly-huynh-W8KTS-mhFUE-unsplash.jpg?alt=media&token=c754dcd9-5bb6-422b-bba2-35b40f1b047f", "https://firebasestorage.googleapis.com/v0/b/replay-chat-dd920.appspot.com/o/next-images%2Fmilad-fakurian-nY14Fs8pxT8-unsplash.jpg?alt=media&token=88ac6424-3b03-4da2-8099-2768d11cce35", "https://firebasestorage.googleapis.com/v0/b/replay-chat-dd920.appspot.com/o/next-images%2Fpawel-czerwinski-vI5XwPbGvmY-unsplash.jpg?alt=media&token=bc94569f-c146-44d3-9e95-4334783e77c1", "https://firebasestorage.googleapis.com/v0/b/replay-chat-dd920.appspot.com/o/next-images%2Fpawel-czerwinski-prMn9KINLtI-unsplash.jpg?alt=media&token=e437b711-3518-4fbe-a6de-ab6950908277", ]; const placeholders = await Promise.all( imageUrls.map(async (url) => { const { base64 } = await getPlaiceholder(url); return base64; }) ); return ( <div> {imageUrls.map((url, index) => { return ( <Image key={url} src={url} alt="Vercel Logo" blurDataURL={placeholders[index]} placeholder="blur" width={420} height={220} /> ); })} </div> ); }
Result:
Screen.Recording.2022-11-01.at.10.22.22.PM.mov This example uses React Server Components (which you can use inside the new app directory) so if your app is using the pages directory, you will have to generate the placeholders in either of getStaticProps or getServerSideProps or inside an API route (Plaiceholder works in a nodejs environment, not in the browser).
Let me know if you guys have any questions about this explanation.
Thanks for your solution, it works fine. Can you please explain why there is no longer support for simple thumbnail url and we force to use base64 ?
Also I couldn't find any note about Sharp
in the docs for image component but there is a warning when runing server in production.
I think it's good to add some note about Sharp
in the docs.
To add to this issue, the blur style is not removed when the image is loaded, so any non-square transparent bg will show the background blur behind it.
To add to this issue, the blur style is not removed when the image is loaded, so any non-square transparent bg will show the background blur behind it.
The same issue.
Same issue with the blur not being removed. Example: https://dev.goodparty.org/
(Next 13.0.6)
I've only blurDataUrl with no blur effect - on Next 13.1.0.
Here is my code :
<Image
placeholder='blur'
blurDataURL='/placeholder.jpg'
className={clsx('absolute top-0 left-0 w-full h-full object-cover rounded-lg')}
src={imgUrl}
alt='movie thumbnail'
width="200"
height="200"
/>
I tried : placeholder='empty' = ok placeholder='blur' = ko
Same issue shimmer effect not working with NextJs13.
We can see difference in example from docs. Legacy version has the effect.
https://image-component.nextjs.gallery/shimmer https://image-legacy-component.nextjs.gallery/shimmer
To add to this issue, the blur style is not removed when the image is loaded, so any non-square transparent bg will show the background blur behind it.
@Igosuki @vadymstebakov could you guys please share a reproducible example with me, I'd like to debug why the blur style is not being removed cause the code handling this is more or less the same as the code handling this in the legacy image component.
@tomer-tgp I can't see the blur style remaining in the example you've shared, perhaps because you've fixed it - would be great if you could help me with an example as well. Thanks!
could you guys please share a reproducible example with me...
I'm also having the blurHash remaining in place post load. @ishaqibrahimbot Here's a CodeSandbox link for a stripped down project. - Next 13.0.7
Addendum: I tried updating and it seems to be working in Next 13.1.6 - But the issue now is that css transitions on the image have no effect. In prior versions (last project was 12.1.7-canary.5) you put img { transition: all 300ms }
in your css and it automagically faded between blurData and img.
To add to this issue, the blur style is not removed when the image is loaded, so any non-square transparent bg will show the background blur behind it.
@Igosuki @vadymstebakov could you guys please share a reproducible example with me, I'd like to debug why the blur style is not being removed cause the code handling this is more or less the same as the code handling this in the legacy image component.
@tomer-tgp I can't see the blur style remaining in the example you've shared, perhaps because you've fixed it - would be great if you could help me with an example as well. Thanks!
Next.js version: 13.0.6.
Here is Codesandbox

Can't do any better than what @vadymstebakov commented
Same issue shimmer effect not working with NextJs13.
We can see difference in example from docs. Legacy version has the effect.
https://image-component.nextjs.gallery/shimmer https://image-legacy-component.nextjs.gallery/shimmer
Next.js 13 always creates a blurry version of blurDataUrl
but in case we need shimmer effect, it would be redundant (and have performance issues in our case) and resulting in a broken shimmer effect.
What should I do to bring it up? Should I open a new issue or feature request?
There should be an option to use blurDataUrl
without modifying. perhaps we should have a better prop name like placeholderDataUrl
and more options in placeholder
prop instead of just empty
& blur
.
I think it would also be good if you could provide a placeholder img when empty
is defined, so that not a base64 encoded gif is used from here. We have pretty strict CSP policies, which forbid data:
references in images. Unfortunately, when empty
is defined, a transparent gif is used: data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7
.
Is there the possibility that next/image
could add support for URLs in the blurDataURL prop?
We use IMGIX with custom query parameters to generate our image placeholders. We don't need to use an external tool because imgix is enough for this.
<Image
src={
'https://my-company-images-prd.imgix.net/public/bg-desktop.png?auto=format'
}
alt="company"
blurDataURL={
// this is our placeholder version using imgix query parameters.
'https://my-company-images-prd.imgix.net/public/bg-desktop.png?auto=format&blur=200&px=24'
}
placeholder="blur"
layout="fill"
objectFit="cover"
/>
For now, we'll use the next/image/legacy.
I faced the same problem and changing the base64 from data:application/octet-stream;base64,....
to data:image/png;base64,...
worked for me.
just use "import Image from 'next/legacy/image'" instead of "import Image from 'next/image'".
just use "import Image from 'next/legacy/image'" instead of "import Image from 'next/image'".
it helped to show blurred preloaded image, thanks