site/api-md: Prerender .md files at build time - #1945
Merged
Merged
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
marcleblanc2
force-pushed
the
ci/vercel-ignore-non-site-changes
branch
from
September 11, 2026 22:22
39534c9 to
ce0775f
Compare
marcleblanc2
requested review from
enriquegh
and removed request for
enriquegh
September 12, 2026 03:50
marcleblanc2
force-pushed
the
marc/site/static-md-routes-and-fluid
branch
from
September 16, 2026 01:26
6f5626b to
2a7c664
Compare
marcleblanc2
enabled auto-merge (squash)
September 16, 2026 02:11
pjlast
approved these changes
Sep 16, 2026
marcleblanc2
force-pushed
the
marc/site/static-md-routes-and-fluid
branch
from
September 17, 2026 00:39
2a7c664 to
4fb3560
Compare
Contributor
Contributor
|
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
force-pushed
the
marc/site/static-md-routes-and-fluid
branch
from
September 17, 2026 00:42
4fb3560 to
4406b69
Compare
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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
.mdresponses: no function invocation, no cold start..mdURLs now return the site's normal 404 page instead of a plain-textNot Found.How
One file changed:
src/app/api/md/[...slug]/route.ts.generateStaticParams()lists every doc page, sonext buildwrites each.mdresponse to disk.dynamicParams = falsemakes unknown slugs 404 at the edge.index.mdxis skipped (empty path;/api/mdwas never a valid URL for this route).Tested
pnpm buildprerenders ~500 markdown pages; build time unchanged (~40 s).pnpm start: known pages return200 text/markdown, unknown pages return404.x-vercel-cache: HITon any.mdURL.Replaces #1912.