← TIL

Route handlers in a Next.js static export have to opt in

2 min read
#nextjs#static-export#app-router

This site is output: "export", which means next build writes plain files and there is no server afterwards. I wanted an RSS feed as a route handler rather than a script that writes into public/, and the build refused until I added one line.

Two rules, and the error message only really tells you about the first one.

A route handler in a static export must be a GET, and it must declare itself static:

export const dynamic = "force-static";

export async function GET() {
  // ...
}

Without force-static, the build treats the handler as dynamic and tells you it cannot be exported. POST, PUT and friends have nowhere to run at all, so they fail outright.

The second rule is the one that actually bites later: the handler runs exactly once, during the build, and its response body is written to disk as a file. Anything you compute inside it is frozen at that moment. new Date() in there is the build time, not the request time. If you want the feed to change, you rebuild.

The file path is the URL, extension included. src/app/feed.xml/route.ts produces /feed.xml, which means you get a directory literally named feed.xml in your source tree. It looks wrong in the file browser every single time and it is correct.

Fear has killed more dreams than failure ever will.
© 2026 Amey Ambade
Houston, TX
currently obsessing over: the long catalog of bossa nova standards