Skip to content

site/api-md: Prerender .md files at build time - #1945

Merged
marcleblanc2 merged 1 commit into
mainfrom
marc/site/static-md-routes-and-fluid
Sep 17, 2026
Merged

marcleblanc2 merged 1 commit into
mainfrom
marc/site/static-md-routes-and-fluid

Conversation

@marcleblanc2

@marcleblanc2 marcleblanc2 commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Every docs page is also served as raw markdown at <page>.md (for LLMs and tooling). Today each of those requests runs a serverless function. This PR generates the markdown files once at build time instead, so Vercel serves them straight from the CDN.

Why

  • Faster .md responses: no function invocation, no cold start.
  • Unknown .md URLs now return the site's normal 404 page instead of a plain-text Not Found.

How

One file changed: src/app/api/md/[...slug]/route.ts.

  • generateStaticParams() lists every doc page, so next build writes each .md response to disk.
  • dynamicParams = false makes unknown slugs 404 at the edge.
  • The root index.mdx is skipped (empty path; /api/md was never a valid URL for this route).

Tested

  • pnpm build prerenders ~500 markdown pages; build time unchanged (~40 s).
  • pnpm start: known pages return 200 text/markdown, unknown pages return 404.
  • Preview deploy: check x-vercel-cache: HIT on any .md URL.

Replaces #1912.

@vercel

vercel Bot commented Sep 11, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
sourcegraph-docs Ready Ready Preview Sep 17, 2026 12:44am UTC

Request Review

@marcleblanc2
marcleblanc2 force-pushed the ci/vercel-ignore-non-site-changes branch from 39534c9 to ce0775f Compare September 11, 2026 22:22
Base automatically changed from ci/vercel-ignore-non-site-changes to main September 11, 2026 22:30
@marcleblanc2
marcleblanc2 requested review from enriquegh and removed request for enriquegh September 12, 2026 03:50
@marcleblanc2
marcleblanc2 force-pushed the marc/site/static-md-routes-and-fluid branch from 6f5626b to 2a7c664 Compare September 16, 2026 01:26
@marcleblanc2 marcleblanc2 changed the title site/api-md: Prerender markdown routes at build time; enable Fluid compute site/api-md: Prerender markdown routes at build time Sep 16, 2026
@marcleblanc2 marcleblanc2 changed the title site/api-md: Prerender markdown routes at build time site/api-md: Prerender markdown at build time Sep 16, 2026
@marcleblanc2 marcleblanc2 changed the title site/api-md: Prerender markdown at build time site/api-md: Prerender .md files at build time Sep 16, 2026
@marcleblanc2
marcleblanc2 enabled auto-merge (squash) September 16, 2026 02:11
@marcleblanc2
marcleblanc2 requested a review from a team September 16, 2026 02:12
@marcleblanc2
marcleblanc2 force-pushed the marc/site/static-md-routes-and-fluid branch from 2a7c664 to 4fb3560 Compare September 17, 2026 00:39
@github-actions

github-actions Bot commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Spell check found 2 issue(s) in this PR

Only findings on lines added by this PR, and in its title and description, are shown.

Pull request title

  • column 14
    • Prerenderpretender

Pull request description

  • line 18, column 16
    • prerenderspretenders

Please correct the spelling, or add words which are correct to cspell-allow-list.txt.

Run npx cspell@10 --no-progress --dot '**/*' locally to check the full repository.

Every doc page's markdown is now written as a static file during the
build, so .md requests are served from the CDN instead of invoking a
serverless function per request. Unknown slugs 404 without a function
call (dynamicParams = false).

Co-authored-by: Amp <amp@ampcode.com>
Amp-Thread-ID: https://ampcode.com/threads/T-01a0a7d0-966f-7278-9368-707fc1017ab6
@marcleblanc2
marcleblanc2 force-pushed the marc/site/static-md-routes-and-fluid branch from 4fb3560 to 4406b69 Compare September 17, 2026 00:42
@marcleblanc2
marcleblanc2 merged commit 35d9d97 into main Sep 17, 2026
6 checks passed
@marcleblanc2
marcleblanc2 deleted the marc/site/static-md-routes-and-fluid branch September 17, 2026 00:45
marcleblanc2 added a commit that referenced this pull request Sep 17, 2026
Every Vercel build spent 25-40s of its ~2 minutes re-rendering all 506
MDX documents, even when they didn't change, because contentlayer2's
cache lived in `.contentlayer/.cache` and Vercel only keeps
`.next/cache` between builds

## Changes

- `pnpm run build` now runs contentlayer2 as its own step
(`dev/build-content.mjs`) with its cache under `.next/cache`, so a
deploy re-renders only the documents that changed

- Upstream Contentlayer2 decides "changed" by comparing each file's
`mtime`, but Vercel builds use fresh clone, which sets every `mtime` to
the clone time, so the restored cache never hit
- `dev/build-content.mjs` now sets each `.mdx` file's `mtime` from a
hash of its content first
- Opened timlrx/contentlayer2#94 to fix this upstream; drop this
workaround once the fix is shipped

- This removes the only reason `next build` was still holding onto
webpack, so we switched it to using Vercel's Turbopack for faster builds
and better caching

- This upgrade to Turbopack required 2 fixes in our code:
  - `src/data/redirects.ts` is imported instead of `require()`
- `contentlayer.config.ts` imports the Shiki theme as JSON instead of by
a relative path

## Vercel build times

| Step | Cold build on `main` | Warm build on this branch |
| --- | --- | --- |
| contentlayer | ~40s | 3s |
| compile | ~19s | 4.7s |
| TypeScript | 7s | 4.5s |
| static pages | ~37s | ~27s (1512 pages since #1945) |
| build traces | ~8s | – |
| **Build Completed** | **2m** | **49s** |

- The first build after any change to this script is still cold (its
restored cache holds the old hashes)
  - Every build after is warm

- Also checked locally:
- Touching every `.mdx` without changing content still hits the cache
(8.5s → 2.8s)
  - Changing one file re-renders only that file
- `next start` serves docs pages, `/api/og`, `.md` rewrites, redirects,
sitemap, `/api/releases`, 404
  - Shiki output identical to webpack
  - `tsc`, lint and `check-redirects` unchanged from `main`

---------

Co-authored-by: Amp <amp@ampcode.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants