Blur Placeholder Images with Next.js and mdx-bundler
Let's look at adding blur to your images using Next.js
and mdx-bundler.
To do this, we need to create a custom rehype plugin
to get necessary props for next/image.
We need:
widthheightplaceholder='blur'blurDataURL
First, Lazar Nikolov shows us how to
generate blurDataURL for remote images in Next.js
with his rehypeImage function.
His function can easily be adapted for mdx-bundler.
Note: rehypeImage will require some dev dependencies.
yarn add -D plaiceholder util unist-util-visit image-size
When you prepare your MDX, add the custom rehype plugin.
import {bundleMDX} from 'mdx-bundler';
import rehypeImage from '@lib/imageMetadata';
export async function prepareMDX(source: string) {
return bundleMDX({
source,
cwd: process.cwd(),
esbuildOptions(options) {
options.target = 'esnext';
return options;
},
xdmOptions(options) {
options.remarkPlugins = [...(options.remarkPlugins ?? [])];
options.rehypePlugins = [...(options.rehypePlugins ?? []), rehypeImage];
return options;
},
});
}
mdx-bundler will gather the src and alt props from the markdown itself.

The rehypeImage function (found on Lazar's post) will get the placeholder, blurDataURL,
width, and height props.
Now that mdx-bundler is getting the props we need,
we can spread them onto our custom MDX image component.
import {HTMLProps} from 'react';
import {getMDXComponent} from 'mdx-bundler/client';
import Image, {ImageProps} from 'next/image';
function Img(props: HTMLProps<HTMLImageElement>) {
return <Image {...(props as ImageProps)} layout={'responsive'} />;
}
export default function BlogPost({code}: {code: string}) {
const Component = React.useMemo(() => getMDXComponent(code), [code]);
return (
<article>
<Component components={{img: Img}} />
</article>
);
}
All together, you'll get the image with the blur effect.

How to get a smooth blur up effect with Next.js images
One feature I liked with gatsby-plugin-image
was how the image would transition from blurred to high resolution.
Currently, next/image goes from 100% blur to 0% with no transition time,
which looks a bit sloppy in my opinion.
I found a Codepen that shows how you can use JavaScript and CSS to smoothly transition from fully blurred to high-resolution. We'll adapt this to our use-case.
First, if you look at the source code
for next/image, we can see they're using the CSS property filter for the
blur effect.
In our CSS, we'll create the class img-blur,
which replicates the blur created by next/image.
.img-blur {
filter: blur(20px);
}
Then, we'll make an animation to transition smoothly to the high quality image.
.unblur {
animation: unblur 0.3s linear;
}
@keyframes unblur {
from {
filter: blur(20px);
}
to {
filter: blur(0);
}
}
Now, we want to trigger the unblur animation
after the image is fully loaded.
We'll use the onLoadingComplete prop for this purpose.
import {HTMLProps, useState} from 'react';
import Image, {ImageProps} from 'next/image';
function Img(props: HTMLProps<HTMLImageElement>) {
const [blur, setBlur] = useState(true);
return (
<Image
{...(props as ImageProps)}
layout={'responsive'}
className={blur ? 'img-blur' : 'unblur'}
onLoadingComplete={() => setBlur(false)}
/>
);
}
Now, we get the nice blur transition that is sure to delight our users.