Fix: Nextra Not Working — Pages Not Rendering, Sidebar Missing, or MDX Components Broken
Quick Answer
How to fix Nextra documentation site issues — Next.js integration, _meta.json sidebar configuration, custom MDX components, search setup, theme customization, and static export.
The Problem
Nextra pages render as blank or show a 404:
GET /docs/getting-started → 404 Not FoundOr the sidebar doesn’t show your pages:
Sidebar is empty even though MDX files exist in the pages directoryOr MDX components don’t render:
<Callout type="warning">Important note</Callout>
<!-- Renders as plain text instead of a styled callout -->Or search returns no results:
Search box appears but typing returns "No results found"Why This Happens
Nextra is a Next.js-based documentation framework that turns MDX files into a documentation site. It uses file-system routing with metadata files for navigation:
- Nextra 3+ uses App Router — Nextra 3 moved from Pages Router to App Router. The file structure, configuration, and theme setup changed significantly. Using Nextra 2 patterns with Nextra 3 (or vice versa) causes pages to not render.
_meta.jsoncontrols the sidebar — each directory needs a_meta.jsonfile that defines the order and titles of pages in the sidebar. Without it, pages exist but don’t appear in navigation. The file maps filenames (without extension) to display titles.- MDX components must be provided via
mdx-components.tsx— Nextra’s built-in components (Callout,Tabs,Steps) need to be registered. In Nextra 3, this happens through themdx-components.tsxfile at the project root. - Search requires Flexsearch setup — Nextra’s built-in search uses Flexsearch to index page content at build time. If the search configuration is missing or the build step that generates the index fails, search returns empty results.
Fix 1: Set Up Nextra 3 (App Router)
npm install nextra nextra-theme-docs// next.config.mjs
import nextra from 'nextra';
const withNextra = nextra({
// Nextra options
});
export default withNextra({
// Next.js config
});// mdx-components.tsx — project root (required for App Router)
import { useMDXComponents as getDocsMDXComponents } from 'nextra-theme-docs';
const docsComponents = getDocsMDXComponents();
export function useMDXComponents(components) {
return {
...docsComponents,
...components,
};
}// app/layout.tsx
import { Footer, Layout, Navbar } from 'nextra-theme-docs';
import { Head } from 'nextra/components';
import { getPageMap } from 'nextra/page-map';
export const metadata = {
title: { template: '%s — My Docs' },
description: 'Documentation for My Project',
};
export default async function RootLayout({ children }) {
return (
<html lang="en" suppressHydrationWarning>
<Head />
<body>
<Layout
navbar={<Navbar logo={<span style={{ fontWeight: 800 }}>My Docs</span>} />}
pageMap={await getPageMap()}
docsRepositoryBase="https://github.com/user/repo/tree/main"
footer={<Footer>MIT {new Date().getFullYear()} © My Project</Footer>}
>
{children}
</Layout>
</body>
</html>
);
}# Directory structure
app/
├── layout.tsx
├── page.mdx # Home page
├── docs/
│ ├── _meta.ts # Sidebar config for docs/
│ ├── page.mdx # /docs
│ ├── getting-started/
│ │ ├── _meta.ts
│ │ ├── page.mdx # /docs/getting-started
│ │ └── installation/
│ │ └── page.mdx # /docs/getting-started/installation
│ └── api-reference/
│ ├── _meta.ts
│ └── page.mdxFix 2: Sidebar Configuration with _meta
// app/docs/_meta.ts
export default {
'getting-started': 'Getting Started',
'api-reference': 'API Reference',
'guides': 'Guides',
'---': {
type: 'separator',
title: 'Advanced',
},
'architecture': 'Architecture',
'contributing': 'Contributing',
// External link
'github': {
title: 'GitHub',
href: 'https://github.com/user/repo',
newWindow: true,
},
};
// app/docs/getting-started/_meta.ts
export default {
index: 'Overview', // page.mdx in this directory
installation: 'Installation',
configuration: 'Configuration',
'quick-start': 'Quick Start',
};
// Hide a page from sidebar but keep it accessible
export default {
'internal-page': {
display: 'hidden',
},
};Fix 3: Built-In MDX Components
{/* app/docs/getting-started/page.mdx */}
import { Callout, Tabs, Steps, Cards, Card, FileTree } from 'nextra/components';
# Getting Started
## Prerequisites
<Callout type="info">
You need Node.js 18+ installed on your machine.
</Callout>
<Callout type="warning">
**Breaking change in v3:** The configuration format has changed. See the migration guide.
</Callout>
<Callout type="error">
Do not use this in production without proper authentication.
</Callout>
## Installation
<Tabs items={['npm', 'yarn', 'pnpm']}>
<Tabs.Tab>
```bash
npm install my-package
```
</Tabs.Tab>
<Tabs.Tab>
```bash
yarn add my-package
```
</Tabs.Tab>
<Tabs.Tab>
```bash
pnpm add my-package
```
</Tabs.Tab>
</Tabs>
## Setup Steps
<Steps>
### Install dependencies
Run the install command for your package manager.
### Create config file
Create `my-config.ts` in the root of your project.
### Start the dev server
```bash
npm run devProject Structure
Related Resources
Fix 4: Theme Customization
// app/layout.tsx — theme configuration via Layout props
import { Layout, Navbar } from 'nextra-theme-docs';
<Layout
navbar={
<Navbar
logo={
<>
<img src="/logo.svg" width={24} height={24} />
<span style={{ marginLeft: '8px', fontWeight: 800 }}>My Docs</span>
</>
}
projectLink="https://github.com/user/repo"
/>
}
sidebar={{ defaultMenuCollapseLevel: 1 }}
toc={{ float: true, title: 'On This Page' }}
editLink="Edit this page on GitHub"
feedback={{ content: 'Question? Give us feedback' }}
docsRepositoryBase="https://github.com/user/repo/tree/main"
>
{children}
</Layout>/* app/globals.css — custom styling */
:root {
--nextra-primary-hue: 212; /* Blue primary color */
--nextra-primary-saturation: 100%;
}
/* Custom code block theme */
.nextra-code-block {
border-radius: 8px;
}Fix 5: Search Configuration
// next.config.mjs — enable search
import nextra from 'nextra';
const withNextra = nextra({
search: {
codeblocks: true, // Index code blocks
},
});
export default withNextra({});Nextra’s search indexes content at build time using Flexsearch. If search returns empty:
- Run a full build:
npm run build— the search index is generated during build - In development, search may not index all pages until they’ve been visited
- Check that pages have text content — empty MDX files won’t appear in search
Fix 6: Static Export and Deployment
// next.config.mjs — static export for GitHub Pages, Cloudflare Pages, etc.
import nextra from 'nextra';
const withNextra = nextra({});
export default withNextra({
output: 'export',
images: { unoptimized: true },
// For GitHub Pages subdirectory
// basePath: '/my-repo',
});# Build static site
npm run build
# Output is in the 'out' directory
# Deploy to any static host
# Cloudflare Pages
npx wrangler pages deploy out
# GitHub Pages — use GitHub Actions# .github/workflows/deploy.yml
name: Deploy Docs
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
permissions:
pages: write
id-token: write
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 20 }
- run: npm ci && npm run build
- uses: actions/upload-pages-artifact@v3
with: { path: out }
- uses: actions/deploy-pages@v4Still Not Working?
Pages return 404 — in Nextra 3 (App Router), pages must be named page.mdx inside a directory, not filename.mdx directly. For example, /docs/getting-started requires app/docs/getting-started/page.mdx, not app/docs/getting-started.mdx.
Sidebar is empty — every directory that should appear in the sidebar needs a _meta.ts (or _meta.json) file. The keys in the meta file must match the subdirectory names (not file names). If using Nextra 2, the format is _meta.json in the pages/ directory.
MDX components render as plain text — the mdx-components.tsx file must exist at the project root and export useMDXComponents. Without it, custom components from nextra/components aren’t registered and render as undefined HTML elements.
Build fails with “Cannot find module ‘nextra-theme-docs’” — make sure both nextra and nextra-theme-docs are installed. If using Nextra 3, check you’re not accidentally importing from Nextra 2’s API paths. The import structure changed between versions.
For related documentation and content issues, see Fix: MDX Not Working and Fix: Astro Not Working.
Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.
Was this article helpful?
Related Articles
Fix: Fumadocs Not Working — Pages Not Found, Search Not Indexing, or MDX Components Missing
How to fix Fumadocs documentation framework issues — Next.js App Router setup, content source configuration, sidebar generation, MDX components, search, OpenAPI integration, and custom themes.
Fix: Clerk Not Working — Auth Not Loading, Middleware Blocking, or User Data Missing
How to fix Clerk authentication issues — ClerkProvider setup, middleware configuration, useUser and useAuth hooks, server-side auth, webhook handling, and organization features.
Fix: Contentlayer Not Working — Content Not Generated, Types Missing, or Build Errors
How to fix Contentlayer and Contentlayer2 issues — content source configuration, document type definitions, MDX processing, computed fields, Next.js integration, and migration to alternatives.
Fix: Docusaurus Not Working — Build Failing, Sidebar Not Showing, or Plugin Errors
How to fix Docusaurus issues — docs and blog configuration, sidebar generation, custom theme components, plugin setup, MDX compatibility, search integration, and deployment.