Configuration

Everything about md.config.ts, route patterns, handler functions, and integration strategies.

md.config.ts

The configuration file is the single source of truth for all your markdown routes. It exports an array of route handlers created with createMdVersion:

import { createMdVersion } from 'next-md-negotiate'; export const mdConfig = [ createMdVersion('/', async () => { return '# Home\n\nWelcome to our site.'; }), createMdVersion('/products/[productId]', async ({ productId }) => { const p = await db.products.find(productId); return `# \${p.name}\n\n**Price:** $\${p.price}`; }), createMdVersion('/blog/[...slug]', async ({ slug }) => { const post = await getPost(slug); return post.markdown; }), ];

createMdVersion

The main function for defining markdown routes. It takes a pattern, a handler, and optional configuration:

createMdVersion(pattern, handler, options?)

Parameters

ParamTypeDescription
patternstringNext.js-style route pattern (/path, /path/[param], /path/[...slug])
handler(params) => Promise<string>Async function that receives extracted params and returns markdown
optionsobjectOptional configuration (see below)

Options

OptionTypeDefaultDescription
hintTextstringdefault messageCustom LlmHint message for this route
skipHintbooleanfalseSkip LlmHint injection for this route

Route patterns

Patterns follow Next.js App Router conventions:

// Static '/about' → matches /about // Dynamic '/products/[id]' → matches /products/42 '/[org]/[repo]' → matches /vercel/next.js // Catch-all '/docs/[...slug]' → matches /docs/a/b/c // Root '/' → matches /

Type-safe parameters

TypeScript automatically infers the correct parameter types from your route pattern using the ExtractParams utility type:

// TypeScript knows the exact params '/products/[productId]'{ productId: string } '/[org]/[repo]'{ org: string; repo: string } '/docs/[...slug]'{ slug: string } '/'{}

Integration strategies

There are two ways to connect content negotiation to your Next.js routing: rewrites and middleware.

Strategy 1: Rewrites (recommended)

Uses Next.js native rewrite rules with header conditions. Zero runtime overhead — rewrites are evaluated by the Next.js router before your code runs.

// next.config.ts import { createRewritesFromConfig } from 'next-md-negotiate'; import { mdConfig } from './md.config'; export default { async rewrites() { return { beforeFiles: createRewritesFromConfig(mdConfig), }; }, }

Strategy 2: Middleware

Uses Next.js middleware to intercept requests. Gives you more control but adds slight runtime overhead.

// middleware.ts import { createNegotiatorFromConfig } from 'next-md-negotiate'; import { mdConfig } from './md.config'; const negotiate = createNegotiatorFromConfig(mdConfig); export function middleware(request) { const response = negotiate(request); if (response) return response; }

Comparison

AspectRewritesMiddleware
PerformanceZero overheadSlight overhead
FlexibilityLimitedFull control
Setupnext.config.tsmiddleware.ts
Best forMost projectsCustom logic needed

Route handler

Both strategies route matched requests to an internal handler. For App Router, this lives at app/md-api/[[...path]]/route.ts:

import { createMdHandler } from 'next-md-negotiate'; import { mdConfig } from '@/md.config'; export const GET = createMdHandler(mdConfig);
Pages Router: Use createMdApiHandler instead, which returns a Next.js API route handler compatible with NextApiRequest / NextApiResponse.