v1.1.2 — content negotiation for Next.js

next-md
negotiate

Serve markdown to LLMs. HTML to humans. Same URL.

$ npm install next-md-negotiate
GET/products/42
content negotiation
Accept: text/htmlBrowser
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"/> <meta name="viewport" content="width=device..."/> <link rel="stylesheet" href="/_next/static/css/ a8f2e...c3.css"/> <script src="/_next/static/ chunks/webpack-3b...js" async=""></script> </head> <body class="__variable_ a8c2f __variable_e9b36"> <div id="__next"> <main class="flex min-h- screen flex-col"> <h1>Product 42</h1> <span>$42.00</span> </main> </div> </body> </html>
text/html; charset=utf-8~26 KB
Accept: text/markdownLLM Agent
# Product 42 **Price:** $42.00**Category:** Electronics**In Stock:** Yes A premium electronic productwith exceptional build qualityand innovative features.
text/markdown; charset=utf-8
~257x smaller~101 B
same url · same content · different format

See it in action

One header. That's all it takes.

Terminal
$

Three steps. Zero complexity.

01Configure

Define your markdown routes in a single config file. Each route maps a URL pattern to a handler that returns markdown.

// md.config.ts export const mdConfig = [ createMdVersion( '/products/[productId]', async ({ productId }) => { return `# ${name}` } ) ];
02Negotiate

HTTP Accept header determines the response format. Browsers get HTML. LLM agents requesting markdown get markdown.

// Automatic routing Browser → Accept: text/html renders React page LLM Agent → Accept: text/markdown returns markdown
03Serve

Same URL serves both formats. No duplicate endpoints. No SEO penalties. Crawling budget preserved.

GET /products/42 // Browser sees: Content-Type: text/html Size: ~26 KB // LLM agent sees: Content-Type: text/markdown Size: ~101 B

Built for production

Everything you need. Nothing you don't.

~

Zero Duplicate URLs

Single canonical URL serves both formats. No /api/products/42.md endpoints cluttering your sitemap.

T

Type-Safe Params

Route parameters are inferred from patterns. TypeScript knows [productId] means { productId: string }.

#

SEO Neutral

Search engines only see HTML. No duplicate content penalties. Crawling budget stays intact.

>

LLM Discoverable

Hidden hints tell AI agents markdown is available. They re-request with the right Accept header.

/

Both Routers

Works with App Router and Pages Router. Same config, same patterns, same API.

$

One Command Setup

npx next-md-negotiate init detects your project, creates config, sets up routing. Done.

Live demo

This site runs next-md-negotiate

Every page on this site serves markdown to LLM agents. Try it yourself — request any page with an Accept: text/markdown header.

$ curl -H "Accept: text/markdown" <this-url>