Blitz uses a file-system based router that's built on Next.js.
pages/
are mapped to a URL.
(Pages documentation)api/
are mapped to a URL.
(API routes documentation)app/projects/queries/getProjects.js
query is exposed at
/api/projects/queries/getProjects
The router will automatically route files named index
to the root of the
directory.
app/pages/index.js
→ /
app/pages/blog/index.js
→ /blog
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-post
app/pages/dashboard/settings/username.js
→
/dashboard/settings/username
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"}
Dynamic routes can be extended to catch all paths by adding three dots
(...
) inside the brackets. For example:
app/pages/post/[...slug].js
matches /post/a
, but also /post/a/b
,
/post/a/b/c
and 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"]}
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)
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/create
app/pages/post/[pid].js
- Will match /post/1
, /post/abc
, etc.
But not /post/create
app/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.e query
will be an empty object ({}
).
After hydration, Blitz will trigger an update to your application to
provide the route parameters in the query
object.