Introduction
Nextra 4 was released and I tried to move a site built with Nextra 2 over to Nextra 4, but there were so many changes that the upgrade did not go well.
So instead of updating the Nextra 2 project, I created a Nextra 4 project and decided to migrate the Nextra 2 project into the Nextra 4 project.
In this article I go through the changes required when migrating from Nextra 2 to Nextra 4.
1. Router change: Pages Router → App Router
The biggest change is the router.
| Version | Router | Directory |
|---|---|---|
| Nextra 2 | Pages Router | pages/ |
| Nextra 4 | App Router | app/ |
Nextra 4 supports only the Next.js App Router.
MDX files under pages/ have to be moved to app/.
2. Configuration changes in next.config
Nextra 2
const withNextra = require('nextra')({
theme: 'nextra-theme-docs',
themeConfig: './theme.config.tsx',
})
module.exports = withNextra({})Nextra 4
import nextra from "nextra";
const withNextra = nextra({});
export default withNextra({
turbopack: {
resolveAlias: {
"next-mdx-import-source-file": "./mdx-components.js",
},
},
});Main changes:
- The
themeandthemeConfigoptions are gone - Changed from CommonJS to the ESM import form
- The
resolveAliassetting is required when using Turbopack
3. Migrating the theme configuration: theme.config.tsx → app/layout.tsx
Nextra 2: theme.config.tsx
export default {
logo: <span>My Docs</span>,
project: { link: 'https://github.com/...' },
footer: { text: 'MIT 2024' },
}Nextra 4: app/layout.tsx
import { Footer, Layout, Navbar } from "nextra-theme-docs";
import { Head } from "nextra/components";
import { getPageMap } from "nextra/page-map";
import "nextra-theme-docs/style.css";
const navbar = <Navbar logo={<b>My Documentation</b>} />;
const footer = <Footer>MIT {new Date().getFullYear()} © My Documentation.</Footer>;
export default async function RootLayout({ children }) {
return (
<html lang="ja" dir="ltr" suppressHydrationWarning>
<Head />
<body>
<Layout
navbar={navbar}
pageMap={await getPageMap()}
docsRepositoryBase="https://github.com/..."
footer={footer}
>
{children}
</Layout>
</body>
</html>
);
}Main changes:
- The theme configuration is passed as props of the
Layoutcomponent pageMapis obtained with thegetPageMap()function- The CSS has to be imported explicitly (
nextra-theme-docs/style.css)
4. mdx-components.js is now required
In Nextra 4, an mdx-components.js file at the project root has become mandatory.
import { useMDXComponents as getThemeComponents } from "nextra-theme-docs";
const themeComponents = getThemeComponents();
export function useMDXComponents(components) {
return {
...themeComponents,
...components,
};
}Without this file you get the following error:
Module not found: Can't resolve 'next-mdx-import-source-file'5. Format change for _meta files
Nextra 2: _meta.json (JSON format)
{
"index": "Introduction",
"getting-started": "Getting Started"
}Nextra 4: _meta.js (ESM format)
export default {
index: "Introduction",
"getting-started": "Getting Started",
};The file name has also changed from _meta.json to _meta.js.
6. Placement rules for MDX files
| Version | Example path |
|---|---|
| Nextra 2 | pages/docs/index.mdx |
| Nextra 4 | app/docs/page.mdx or content/docs/index.mdx |
With the App Router you either follow the page.mdx naming convention or use the content/ directory.
7. Changes to import paths
| Purpose | Nextra 4 |
|---|---|
| Theme components | nextra-theme-docs |
| Banner, Head, and so on | nextra/components |
| getPageMap | nextra/page-map |
| Theme CSS | nextra-theme-docs/style.css |
8. Static export configuration
When exporting as a static site, the following settings are required in next.config.ts.
export default withNextra({
output: "export", // output static files into the out/ directory
images: {
unoptimized: true, // disable image optimization for static export
},
});Once you set output: "export", the build result is written into the out/ directory when you run npm run build.
9. Configuring search (Pagefind)
Nextra 4 uses Pagefind
for search.
You have to generate the search index after the build.
Installation
npm install -D pagefindpackage.json configuration
{
"scripts": {
"build": "next build",
"postbuild": "pagefind --site out --output-path out/_pagefind"
}
}The postbuild script runs automatically after npm run build finishes.
Points to note:
--site out: specifies the directory the static export goes to--output-path out/_pagefind: where the search index is written- If you have not set
output: "export", use--site .next/server/app
When search does not work
- Check that
output: "export"is set - Check that the
--sitepath inpostbuildisout - Check that the
out/_pagefind/directory is created after the build
Summary
| Item | Nextra 2 | Nextra 4 |
|---|---|---|
| Router | Pages Router | App Router |
| Theme configuration | theme.config.tsx | app/layout.tsx |
| MDX components | Not needed | mdx-components.js required |
| Meta file | _meta.json | _meta.js |
| nextra configuration | theme, themeConfig | Empty object or mdxOptions |
| Search engine | FlexSearch (built in) | Pagefind (needs configuration) |
| Build output | .next/ | out/ (with static export) |