File-Based Routing
Blitz uses a file-system based router that's built on
Next.js.- All components in
pages/are mapped to a URL. (Pages documentation) - All http handlers in
api/are mapped to a URL. (API routes documentation) - Queries and mutations are automatically exposed as an API endpoint
- The
app/projects/queries/getProjects.jsquery is exposed at/api/projects/queries/getProjects - See the RPC Specification for more details
- The
Index routes
The router will automatically route files named
index to the root of the directory.app/pages/index.js→/app/pages/blog/index.js→/blog
Nested routes
The router supports nested files. If you create a nested folder structure files will be automatically routed in the same way still.
app/pages/blog/first-post.js→/blog/first-postapp/pages/dashboard/settings/username.js→/dashboard/settings/username
Dynamic route segments
To match a dynamic segment you can use the bracket syntax. This allows you to match named parameters.
app/pages/blog/[slug].js→/blog/:slug(/blog/hello-world)app/pages/[username]/settings.js→/:username/settings(/foo/settings)app/pages/post/[...all].js→/post/*(/post/2020/id/title)
Consider the following page
app/pages/post/[pid].js:import {useParam} from "blitz"const Post = () => {const pid = useParam("pid")return <p>Post: {pid}</p>}export default Post
Any route like
/post/1, /post/abc, etc. will be matched by app/pages/post/[pid].js. The matched path parameter will be sent as a query parameter to the page, and it will be merged with the other query parameters.For example, the route
/post/abc will have the following query object:{"pid": "abc"}
Similarly, the route
/post/abc?foo=bar will have the following query object:{"foo": "bar", "pid": "abc"}
However, route parameters will override query parameters with the same name. For example, the route
/post/abc?pid=123 will have the following query object:{"pid": "abc"}
Multiple dynamic route segments work the same way. The page
app/pages/post/[pid]/[comment].js will match the route /post/abc/a-comment and its query object will be:{"pid": "abc", "comment": "a-comment"}
Catch all routes
Dynamic routes can be extended to catch all paths by adding three dots (
...) inside the brackets. For example:app/pages/post/[...slug].jsmatches/post/a, but also/post/a/b,/post/a/b/cand so on.
Note: You can use names other than
slug, such as:[...param]
Matched parameters will be sent as a query parameter (
slug in the example) to the page, and it will always be an array, so, the path /post/a will have the following query object:{"slug": ["a"]}
And in the case of
/post/a/b, and any other matching path, new parameters will be added to the array, like so:{"slug": ["a", "b"]}
Optional catch all routes
Catch all routes can be made optional by including the parameter in double brackets (
[[...slug]]).For example,
app/pages/post/[[...slug]].js will match /post, /post/a, /post/a/b, and so on.The
query objects are as follows:{ } // GET `/post` (empty object){ "slug": ["a"] } // `GET /post/a` (single-element array){ "slug": ["a", "b"] } // `GET /post/a/b` (multi-element array)
Caveats
Predefined routes take precedence over dynamic routes, and dynamic routes over catch all routes. Take a look at the following examples:
app/pages/post/create.js- Will match/post/createapp/pages/post/[pid].js- Will match/post/1,/post/abc, etc. But not/post/createapp/pages/post/[...slug].js- Will match/post/1/2,/post/a/b/c, etc. But not/post/create,/post/abc
Pages that are statically optimized by
Automatic Static Optimization will be hydrated without their route parameters provided, i.equerywill be an empty object ({}).After hydration, Blitz will trigger an update to your application to provide the route parameters in the
queryobject.