I reconstruct my blog with Next.js. The previous site is composed with Nuxt3 but some problems happened.
Environment
- Node.js: v22.13.1
- Next.js: 15.1.6 (App Router)
Use @next/mdx
If you don't use frontmatter (sentences that explain the content on the top of a MD/MDX file such as YAML), @next/mdx
is useful.
Installation
$ npm install @next/mdx @mdx-js/loader @mdx-js/react @types/mdx
ref. Configuring: MDX | Next.js
Remark and Rehype
Some elements, such as table, are not supoorted by @next/mdx
so you should use remark-gfm
.
If you want to highlight the code, it is also neccessary to configure rehype-highlight
.
$ npm i remark-gfm rehype-highlight
const withMDX = createMDX({
options: {
remarkPlugins: [['remark-gfm']],
rehypePlugins: [['rehype-highlight']],
},
})
* According to the documentation, remark and rehype plugins are not supported if you use Turbopack. The below code is not working.
import remarkGfm from 'remark-gfm'
import rehypeHighlight from 'rehype-highlight'
const withMDX = createMDX({
options: {
remarkPlugins: [remarkGfm],
rehypePlugins: [rehypeHighlight],
},
})
Use next-mdx-remote
Installation
@next/mdx
package is provided to convert MDX but the frontmatter section is also displayed.
If you want to show the content that frontmatter exclude, you should use next-mdx-remote
package.
$ npm i next-mdx-remote
const nextConfig: NextConfig = {
transpilePackages: ['next-mdx-remote'],
}
import { MDXRemote } from 'next-mdx-remote/rsc'
const content = 'This is **MDX** style.'
export default async function Page() {
return <MDXRemote source={content} />
}
Probably, 'rsc' means rendering server component. If you directly call MDXRemote
from next-mdx-remote
, it is used as the client component.
ref. GitHub - hashicorp/next-mdx-remote: Load MDX content from anywhere
Call the contents from MDX files
In the documentation, you must use fs
or globby
package to process the MDX files.
import fs from 'fs'
import path from 'path'
...
export function generateStaticParams() {
const dirPath = path.join(process.cwd(), 'src/app/content/')
const posts = fs.readdirSync(dirPath)
return posts.map((n) => {
return { slug: n.replace('.mdx', '') }
})
}
Customize Each HTML Elements
import { MDXRemote } from 'next-mdx-remote/rsc'
const components = {
h2: (props: { children: string }) => (
<h2 className="font-medium text-2xl">{props.children}</h2>
),
}
export function CustomMDX(props: { source: string; [key: string]: any }) {
return (
<MDXRemote
{...props}
components={{ ...components, ...(props.components || {}) }}
/>
)
}
Frontmatter
$ npm i gray-matter
import matter from 'gray-matter'
import { CustomMDX } from '../mdx-remote'
export default async function Page({
params,
}: {
params: Promise<{ slug: string }>
}) {
const slug = (await params).slug
const { data, content } = matter.read(
path.join(process.cwd(), `src/app/content/${slug}.mdx`)
)
return <CustomMDX source={content} />
}
Remark and Rehype
Some elements, such as table, are not converted with next-mdx-remote
package so you should use remark-gfm
.
If you want to highlight the code, it is also neccessary to configure rehype-highlight
.
$ npm i remark-gfm rehype-highlight
import { MDXRemote } from 'next-mdx-remote/rsc'
import remarkGfm from 'remark-gfm'
import rehypeHighlight from 'rehype-highlight'
export function CustomMDX(props: { source: string; [key: string]: any }) {
return (
<MDXRemote
{...props}
components={{ ...components, ...(props.components || {}) }}
options={{
mdxOptions: {
remarkPlugins: [remarkGfm],
rehypePlugins: [rehypeHighlight],
},
}}
/>
)
}
Some articles include LaTeX to write the mathematical formura so I also use these packages that it shows LaTeX as HTML.
- remark-math/packages/remark-math at main · remarkjs/remark-math · GitHub
- remark-math/packages/rehype-katex at main · remarkjs/remark-math · GitHub
Auto-Generate Images for OGP
When I build the app, the below error happened.
Error: export const dynamic = "force-static"/export const revalidate not configured on route "/opengraph-image" with "output: export".
Added the following code in opengraph-image.tsx.
export const dynamic = 'force-static'
ref. Metadata Files: opengraph-image and twitter-image | Next.js
Configuration to Export Static Site
If you use the static rendering mode, the following option is required.
const nextConfig: NextConfig = {
output: 'export',
}
$ npm run build
Start rendering using exported files with the following command.
{
"scripts": {
"start": "npx serve@latest out"
}
}
$ npm start
When you run this command for the first time, serve package is installed.
Import Google Services
Google Analytics
ref. Using Google Analytics with Next.js (through next/script
) | Next.js
Google AdSense
ref. How to implement Google Adsense on App Router (Next.js) | by Kamo Tomoki | Medium
Convert the extension of files from MD to MDX at one time
I used MD files in my blog using Nuxt3, so I converted the files from MD to MDX with the following command.
$ for filename in *.md; do mv "$filename" "${filename%.md}.mdx"; done