---
title: "Next.js — Markdown Mirrors"
description: "Expose .md mirrors of your Next.js pages with App Router or Pages Router."
type: "page"
canonical: "https://audigeo.ai/docs/integrations/markdown-mirrors/nextjs"
---

# Next.js — Markdown Mirrors

## App Router

Create a catch-all route that serves `.md` for any page.

`app/[...slug]/page.md/route.ts`:

```ts
import { NextResponse } from "next/server";
import { promises as fs } from "node:fs";
import path from "node:path";

export async function GET(_req: Request, { params }: { params: { slug: string[] } }) {
  // Source: keep canonical content in /content/{slug}.md alongside your MDX/components.
  const file = path.join(process.cwd(), "content", ...params.slug) + ".md";
  try {
    const md = await fs.readFile(file, "utf-8");
    return new NextResponse(md, {
      headers: {
        "Content-Type": "text/markdown; charset=utf-8",
        "Cache-Control": "public, max-age=3600, must-revalidate",
        "X-Robots-Tag": "index, follow",
      },
    });
  } catch {
    return new NextResponse("Not found", { status: 404 });
  }
}
```

If your page content lives in MDX, render the MDX raw (without React components) and return that:

```ts
import { compile } from "@mdx-js/mdx";
// ... read MDX, strip JSX components manually or use a stripping plugin
```

## Pages Router

`pages/api/[...slug].md.ts` — works similarly with `req.query.slug` and `res.send(md)`.

## llms-full.txt

Add `pages/llms-full.txt.ts`:

```ts
import type { NextApiRequest, NextApiResponse } from "next";

const URLS = ["/", "/about", "/blog/post-1"]; // populate from your sitemap

export default function handler(_req: NextApiRequest, res: NextApiResponse) {
  res.setHeader("Content-Type", "text/plain");
  const lines = ["# My Site — LLM-friendly index", "", ...URLS.map((u) => `- https://example.com${u}.md`)];
  res.send(lines.join("\n"));
}
```

## robots.txt

Allow `*.md` and welcome AI crawlers — see [README](README.md).

## Verify

Run an AudiGEO audit. Target: `markdown_mirror` ≥ 7.
