@stefanprobst/remark-mdx-next-images v1.2.0
remark-mdx-next-images
This is a remark plugin to transform local Markdown images
into next/image components.
It basically does what
next-image-loader
does, but outside of Webpack.
The following Markdown images
Will be transformed: 
Will _not_ be transformed: are converted to
<Image src={{ src: "/image.12345678.png", width: 200, height: 200 }} alt="local example image" title="local example" />
<img src="https://example.com/image.png" alt="remote example image" title="remote example" />How to install
npm i @stefanprobst/remark-mdx-next-images sharpHow to use
Add the plugin to the MDX processor, and make sure to pass in both file path (path) and file
content (value) (see vfile). For further details on how to
render MDX with Next.js, please see the offical guide.
// ./pages/index.js
import { compile, run } from '@mdx-js/mdx'
import withNextImages from '@stefanprobst/remark-mdx-next-images'
import Image from 'next/image'
import { useState, useEffect } from 'react'
import * as runtime from 'react/jsx-runtime'
const markdown = `
Look at *this*!

`
export async function getStaticProps() {
const result = await compile(
{
// Usually, you would pass in the path to the local MDX file here.
path: import.meta.url,
value: markdown,
},
{ outputFormat: 'function-body', remarkPlugins: [withNextImages] },
)
return {
props: {
code: String(result),
data: result.data,
},
}
}
export default function Page(props) {
const { code, data } = props
const { default: Content, ...exported } = useMdx(code)
return (
<main>
<Content components={{ Image }} />
<pre>{JSON.stringify(data, null, 2)}</pre>
<pre>{JSON.stringify(exported, null, 2)}</pre>
</main>
)
}
function useMdx(code) {
const [mdxModule, setMdxModule] = useState({ default: () => null })
useEffect(() => {
run(code, runtime).then(setMdxModule)
}, [code])
return mdxModule
}
// function useMdxSync(code) {
// const mdxModule = useMemo(() => {
// return runSync(code, runtime);
// }, [code]);
// return mdxModule;
// }Additional props for next/image, like
placeholder can be set by passing
a custom Image component via components:
export default function Page(props) {
const { default: Content } = useMdx(props.code)
return <Content components={{ Image: CustomImage }} />
}
function CustomImage(props) {
return <Image {...props} placeholder="blur" />
}Images will be copied to a the Next.js public folder. In most cases you will want to specify a
specific subfolder with the publicDirectory option, so it can safely by added to .gitignore.
Cache headers for images served from that directory can be set via Next.js config.
Frontmatter images
With the images option it is possible to process additional image file paths which have been added
to vfile.data. This is useful to e.g. process a featuredImage frontmatter field:
import { read } from 'to-vfile'
import { matter } from 'vfile-matter'
const vfile = read('./post.md')
/** There are different approaches to parsing frontmatter. `vfile-matter` makes parsed frontmatter accessible on `vfile.data.matter`. */
matter(vfile)
const result = await compile(vfile, {
outputFormat: 'function-body',
remarkPlugins: [
[
withNextImages,
{
images(data) {
return [{ key: 'featuredImage', filePath: data.matter.featuredImage }]
},
},
],
],
})
expect(result.data.images).toEqual({
featuredImage: { src: '/image.0a4b47fd.png', width: 200, heigh: 200, blurDataURL: '...' },
})The resulting data shape matches Next.js StaticImageData.
Other options
images(optional): provide additional image file pathspublicDirectory(optional): output folder path relative to the Next.jspublicfolder, defaults to/name(optional): component name, defaults toImageassetPrefix(optional): same as Next.jsassetPrefix