After reading the fantastic article about having mdx in nextjs I tried to implement the same approach in my nextjs 14 app, and also using the official instructions for custom elements to reformat some tags in my mdx files.
import Image, { ImageProps } from 'next/image';
import type { MDXComponents } from 'mdx/types';
export function useMDXComponents(components: MDXComponents): MDXComponents {
return {
h2: ({ children }) => <h2 className="font-bold">{children}</h2>,
img: (props) => <Image sizes="100vw" {...(props as ImageProps)} />,
// img: (props) => <hr />, This is for testing if it picks img and replaces it with hr
...components,
};
}
Using the config above, for a very simple markdown doc like the following, it will correctly replace the heading2 element (denoted as ##
) with the defined h2
element (The className="font-bold">
is added to my output html document) However, <img>
tag will not be substituted with the nextjs’ <Image />
element (sizes="100vw"
is not added)
I even tried to add a dummy img: (props) => <hr />
for my config to see if it picks up img
tag and replaces it with hr
, but no luck.
My Markdown:
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
## This is my heading 2
<img src="/path/to/my/image.png" width="300" height="300" />
The rendered html:
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
<h2 class="font-bold">This is my heading 2</h2>
<img src="/path/to/my/image.png" width="300" height="300" />
I’m using next-mdx-remote
with the following config in my [...slug]/page.tsx
:
import { compileMDX } from 'next-mdx-remote/rsc';
import rehypeSlug from 'rehype-slug';
import remarkGfm from 'remark-gfm';
export default async function ArticlesPage({ params }: Params) {
// Read the MDX file from the content source directory
const source = fs.readFileSync(path.join(process.cwd(), contentSource, params.slug.join('/')) + '.mdx', 'utf8');
// MDX accepts a list of React components
const components = useMDXComponents({});
// We compile the MDX content with the frontmatter, components, and plugins
const { content, frontmatter } = await compileMDX({
source,
options: {
mdxOptions: {
// rehypePlugins: [rehypeSlug],
// remarkPlugins: [remarkGfm],
},
parseFrontmatter: true,
},
components,
});
const pageTitle = frontmatter.title as string;
const pageLastUpdate = frontmatter.lastUpdate as string;
// Render the page
return (
<>
<h1>{pageTitle}</h1>
<p className="text-sm italic">Last update: {pageLastUpdate}</p>
<div>{content}</div>
</>
);
}