Route path
s are used to match parts of a URL's pathname to a route. At their core, route paths are just strings and can be defined using a variety of syntaxes, each with different behaviors. Before we get into those behaviors, let's look at a few important cross-cutting path concerns.
To make things extremely simple, route paths ignore leading and trailing slashes. You can include them if you want, but they do nothing 😜. The following are all valid paths:
/
/about
about/
about
$
/$
/$/
Inner path slashes are 100% valid and can be used to target paths without creating additional component hierarchy or markup.
For example, a path of blog/post/edit
could be used to render a blog post editor without nesting it inside of other components:
const rootConfig = new RootRoute()const editRoute = new Route({ path: `blog/post/edit`, component: PostEditor })
const routeConfig = rootRoute.addChildren([editRoute])
This router setup would only render a single <PostEditor />
component for the blog/post/edit
path.
Route paths are not case-sensitive by default. This means that /about
and /AbOuT
are considered the same path out-of-the box. This is a good thing, since this is the way most of the web works anyway! That said, if you truly want to be weird and match a path with a different case, you can set a route's caseSensitive
property to true
.
A route with a path of /
is called an "index" path because it specifically targets the state of a parent route when no child route is matched. This is best understood through an example:
let rootRoute = new RootRoute()
// ✅ This is the index route for the entire router// It will only display when the path is `/`const indexRoute = new Route({ getParentRoute: () => rootRoute, path: '/' })
const blogRoute = new Route({ getParentRoute: () => rootRoute, path: 'blog' })
// ✅ This is the index route for the `/blog` route// It will only display when the path is `/blog`const blogIndexRoute = new Route({ getParentRoute: () => blogRoute, path: '/' })
const routeConfig = rootRoute.addChildren([ indexRoute, blogRoute.addChildren([blogIndexRoute]),])
If a route has any children, it is uncommon not to also have an index route. This is because the index route is the only way to render a component when the parent route is matched, but no child route is matched.
Static paths are the simplest type of route path. They are just a string that matches the beginning of a URL's pathname. For example:
let rootRoute = new RootRoute()
// ✅ This route will match any path that starts with `/about`const aboutRoute = new Route({ getParentRoute: () => rootRoute, path: 'about',})
const routeConfig = rootRoute.addChildren([aboutRoute])
A route path segment that starts with a $
followed by a label is called a "dynamic segment" and captures that section of the URL into the params
object for use in your application. For example:
let rootRoute = new RootRoute()
const usersRoute = new Route({ getParentRoute: () => rootRoute, path: 'users',})
// ✅ This path will capture anything in the path after `/users` and before the next slash// eg. `/users/123` and `/users/123/details will both capture `123`const userRoute = new Route({ getParentRoute: () => usersRoute, path: '$userId',})
const routeConfig = rootRoute.addChildren([usersRoute.addChildren([userRoute])])
Dynamic segments can be accessed via the params
object using the label you provided as the property key. For example, a path of /users/$userId
would produce a userId
param of 123
for the path /users/123/details
:
{ userId: '123'}
A route with a path of only *
or $
is called a "splat" route because it always captures any remaining section of the URL from the *
/$
down. For example:
let rootRoute = new RootRoute()
// This route will match any path that starts with `/file`// For example, it will match both `/file` and `/file/documents/hello-worldconst fileBaseRoute = new Route({ getParentRoute: () => rootRoute, path: 'file/*', // or `file/$`})
const routeConfig = rootRoute.addChildren([fileBaseRoute])
Splat routes capture their matched path in the params
object under the *
property. For example, if the path is /file/documents/hello-world
, the params
object would look like this:
{ '*': 'documents/hello-world'}
🧠Why use both
*
and$
? Thanks to tools like Remix, we know that while*
s are the most common character to represent a wildcard/splat, they do not play nice with filenames or CLI tools. In those cases$
can be a better choice.
A 404 / non-matching route is really just a fancy name for a Splat / Catch-All path. If no other routes match, the splat/catch-all route will always match
Pathless routes are routes that do not have a path
and instead an id
to uniquely identify them. Pathless routes do not use path segments from the URL pathname, nor do they add path segments to it during linking. They can be used to:
loader
requirement before displaying any child routesTo create a pathless route, define a route with an id
property instead of a path
:
const rootRoute = new Route()
// Our layout routeconst layoutRoute = new Route({ getParentRoute: () => rootRoute, id: 'layout',})
const layoutARoute = new Route({ getParentRoute: () => layoutRoute, path: 'layout-a',})
const layoutBRoute = new Route({ getParentRoute: () => layoutRoute, path: 'layout-b',})
const routeConfig = rootRoute.addChildren([ layoutRoute.addChildren([layoutARoute, layoutBRoute]),])
In the above example, the pathless route will not add or match any path in the URL, but will wrap the layout-a
and layout-b
routes with any elements or logic defined in it.
🧠An ID is required because every route must be uniquely identifiable, especially when using TypeScript so as to avoid type errors and accomplish autocomplete effectively.
Search Params by default are not used to identify matching paths mostly because they are extremely flexible, flat and can contain a lot of unrelated data to your actual route definition. However, in some cases you may choose to use them to uniquely identify a route match. For example, you may want to use a search param to identify a specific user in your application, you might model your url like this: /user?userId=123
. This means that your user
route would need some extra help to identify a specific user. You can do this by adding a key
function to your route:
const userRoute = new Route({ getParentRoute: () => usersRoute, path: 'user', key: ({ search }) => search.userId,})
Route paths are just the beginning of what you can do with route configuration. We'll explore more of those features later on.
Your weekly dose of JavaScript news. Delivered every Monday to over 100,000 devs, for free.