Deviopath › Next.js

App Router URL Resolver

Hard · Routing

In the App Router, folders define URL segments and a page.tsx makes a segment publicly reachable. Folder conventions:

- [slug] is a dynamic segment: it matches one URL segment, params.slug is a string.

- [...slug] is a catch-all: it matches one or more remaining segments, params.slug is an array.

- [[...slug]] is an optional catch-all: like catch-all, but it also matches zero segments.

- (group) folders are route groups: they organize files and are not part of the URL.

- _folder folders are private: they and everything inside are never routes.

Write resolveRoute(files, url). This is a simplified model of how the App Router matches a URL. files lists project files (only files named page.<ext> under app/ are pages). url may contain a query string and a trailing slash; ignore both.

Match segment by segment, depth-first. At each level try, in priority order: a static folder with exactly the same name, then a dynamic segment, then a catch-all, then an optional catch-all. If the more specific choice cannot match the rest of the URL, backtrack and try the next one. A catch-all or optional catch-all is always the last folder and takes all remaining segments. When the URL is used up, a page that ends there matches; otherwise an optional catch-all at that level matches zero segments (Next.js refuses to build a page and an optional catch-all at the same level, so both never exist together).

Return { page, params } with the page file path, or null. For an optional catch-all that matched nothing, leave the param out (Next.js gives it the value undefined).

Next: A/B Test with a Rewrite in proxy.ts