A minimal Next.js 15 application that renders content from Markdown files.
- Render Markdown files as dynamic pages
- Add React components anywhere in your Markdown
- The folder structure becomes the URL path
- Global styles using Tailwind CSS and DaisyUI
See a live example at nextjs-markdown-boilerplate.vercel.app/
I'm lazy, so I made this extremely simple.
Just write some markdown in a .mdx file, and it will automatically become a properly styled page.
# My Page
This is my page.Add new pages by creating .mdx files in the app/content directory. The file name becomes the URL path:
app/content/index.mdx→/app/content/a-beautiful-page.mdx→/a-beautiful-pageapp/content/more-content/another-page.mdx→/more-content/another-page
Add custom React components to the components directory. Then, import them at the top of an MDX file and use them like any other React component.
import MyComponent from '../components/my-component'
**Hey**, here's a component:
<MyComponent />
*And here's some more markdown content.*In Next.js 15, dynamic APIs like params and searchParams are now asynchronous. The boilerplate handles this automatically in the page component, but if you add custom logic:
// In a page or layout
export default async function Page({ params, searchParams }) {
const { slug } = await params // Await params
const { query } = await searchParams // Await searchParams
// Your code
}For static behavior, avoid using cookies(), headers(), draftMode() in layouts or pages unless necessary.
This boilerplate is static-first: pages are pre-rendered at build time. If you need dynamic features (e.g., user-specific content), opt into dynamic rendering by using the async APIs above or adding export const dynamic = 'force-dynamic' to a page.
- SEO/Sitemap: Add
next-sitemapfor automatic sitemap generation. - Analytics: Integrate with Vercel Analytics or Google Analytics.
- Testing: Add Vitest with
npm install -D vitest @testing-library/react. - Linting: Run
npm run lint(already configured). - Static Export: For non-server deployments, add
output: 'export'tonext.config.mjs(note: disables server features).
- Node.js 18+
- npm 9+
- TypeScript
- React 19
- Next.js 15
I hear you. Much simpler markdown rendering solutions abound, but I'm stuck with React bloat because:
-
I want to edit the pages sometimes, and I want to do so using markdown without extra steps.
-
I still want to use React components *sometimes*.
-
I want it to be really easy to deploy and monitor. Nextjs is really easy for those things.
Solving the first issue and third issues don’t require React, but the second definitely does. More discussion about this on Hacker News
npm installnpm run devVisit http://localhost:3000
npm run build
npm startI tried to make this as simple as possible, given the constraints of modern web development.
my-markdown-app/
.
├── README
├── app
│ ├── [...slug] # Dynamic route for all pages
│ │ └── page.tsx # Page component
│ ├── content # **This is the only folder you need to worry about.**
│ │ ├── more-content # Example of a nested folder
│ │ │ └── another-page.mdx # Another page, routes to /more-content/another-page
│ │ └── index.mdx # Home page content
│ ├── globals.css # Global styles and Tailwind imports
│ ├── layout.tsx # Root layout with shared styling
│ └── page.mdx # Home page content (renders index.mdx at root)
├── components # Add custom React components in this directory
├── mdx-components.tsx # Sets up MDX components
├── next.config.mjs # Next.js configuration
├── package.json # Project dependencies
├── postcss.config.js # PostCSS configuration
├── tailwind.config.js # Tailwind CSS configuration
└── tsconfig.json # TypeScript configurationPull requests are welcome.
MIT
- Upgrade to latest stable: Next.js 15.5.2, React 19.1.1, @next/mdx 15.5.2, @mdx-js/loader 3.1.1, DaisyUI 5.0.54, PostCSS 8.5.6, Autoprefixer 10.4.21, ESLint 9.34.0, eslint-config-next 15.5.2, updated React type packages.
- React 19 compatibility: error boundary uses
unknownfor error prop with safe access. - Next 15 compatibility: async
paramsnormalization in catch‑all page. - Static-first defaults preserved; no experimental flags.
- Initial release
- Render Markdown files as dynamic pages
- Add React components anywhere in your Markdown
- The folder structure becomes the URL path
- Global styles using Tailwind CSS and DaisyUI