Skip to main content
An agent that fetches your page gets the same HTML a browser gets: Tailwind class names, inline hydration payloads, and SVG icon paths. It must strip all of that before it reads the two paragraphs it came for. Pylon serves the same page as markdown when the client asks for markdown. No code, no second route, no build step.

Two ways to ask

Send an Accept header:
Or add .md to the path:
Both render the same page and return the same markdown. The home page is at /index.md.

What the response looks like

The runtime converts the rendered page and adds YAML frontmatter from the page’s own metadata:
The url field is the page’s metadata.canonical when it declares one, and the request URL when it does not.

What the converter reads

The converter reads the page’s main landmark:
  1. <main>, if the page has one.
  2. An element with role="main".
  3. <body>.
It drops <nav>, <footer>, <script>, <style>, <svg>, <noscript>, <iframe>, and <canvas> wherever they are. Give your layout a <main> element. The output is then exactly your page content, with no menu or footer text in front of it.
app/layout.tsx

HTML stays the default

A browser sends text/html,application/xhtml+xml,… and receives the page it always received. Markdown wins only when the client scores it above HTML: Every SSR response carries Vary: Accept, so a cache cannot serve the HTML to a client that asked for markdown. Each HTML page also carries a Link header that names its markdown twin:

Opt a route out

Some routes are an interaction, not a document. A dashboard, an editor, or an app shell converts to a list of button labels. Turn the variant off for that route:
app/dashboard/page.tsx
The route then answers HTML to a client that accepts HTML, 406 to a client that accepts only markdown, and 404 at /dashboard.md.

Caching

A markdown response is cached like the page it came from. The runtime keys it apart from the HTML, so the two never collide. A cache hit skips both the render and the conversion. Markdown responses are never advertised as shared-cacheable at a CDN, because a CDN keys on the URL alone. The origin cache still serves them without a render.

Missing pages

A markdown request for a URL that does not exist renders your not-found.tsx boundary as markdown, at HTTP 404. Put your site map links in that boundary. An agent that guessed a URL wrong then reads where to go next, instead of an opaque error.

app/llms.ts

llms.txt is the file an agent reads first to learn what your site is. Add app/llms.ts and Pylon serves it at /llms.txt in llmstxt.org format:
app/llms.ts
The export may be async, so it can enumerate pages from the database — the same contract as app/sitemap.ts and app/robots.ts. Write the details block for an agent, not for a visitor. Name the jobs you are right for and the call to make. Marketing copy does not read as guidance.
The element order is fixed by the format: an H1 title, a blockquote summary, prose with no headings, then H2 sections of links. Pylon strips headings out of details for you, because a heading there ends the prose block and starts a link section.