Replies: 2 comments 1 reply
-
I think this will be doable once RR uses a tree structure to match routes. The constraints would determine which branch to follow for matching. |
Beta Was this translation helpful? Give feedback.
1 reply
-
I'm bumping into more and more limitations with the current way of defining routes using file names so I love the finer grained control in this proposed solution. Issues I'm bumping into now:
|
Beta Was this translation helpful? Give feedback.
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
Proposal
Add segment constraint support to the route definition to allow matching multiple routes in the same segment, but using constraints to decide which one to use.
Background
Right now, if two routes match the same segment Remix Router throws an error, for example, let's say we want to have the routes
/:articleId
/:username
At the moment, the only way to achieve this is by defining the routes as a single route.
While this works, it's not ideal because now our component has to know how to render both an article and a user, and the loader has to know how to render both too.
Use Case
API/Examples
Route Constraints interface
I propose to create a
RouteConstraints
interface that can be used to define constraints for a route.This can be added to the definition of a route in React Router or as a route module export in Remix.
React Router API
In React Router, we can add a
constraints
attribute to the route definition.With this, React Router can use the constraints to ensure the route match the path segment.
One route and with constraints
Following the example above, if we have a single route matching our path segment, and it has constraints, React Router check if the constraints pass.
If they pass, it will use the route, if they don't it will consider it a 404.
Multiple routes, only one with constraints
Let's say now we add a second route.
In this case we have one route with constraints and another one without. Here React Router will need to first check the route with constraints.
If the constraints pass, it will use that route, if they don't it will use the other route, essentially the
:username
route will work as a fallback for the segment, so this segment will always have a match.Multiple routes, all with constraints
Now let's say we decide to add a constraint to our
:username
route.Because both routes has constraints, React Router will check them all.
Constraints as functions
Sometimes, the constraint will need data, let's say we want to have
/whatever
match a dynamically generated landing page, and if the landing page doesn't exist it should match a user profile.Here, the
slug
param constraint is a function that receives the param, and the loader args, and returns a boolean, or a promise that resolves to a boolean.If the constraint is a promise, React Router will wait for it to resolve before checking the result.
The params constraint function will let the application developer do a more advanced check, e.g. checking if the record exists in the database. Since this will block route matching, it must be a fast operation, ideally a cached lookup.
In this case, since the function one can include dynamic data, it will be preferred over the RegExp-based constraint, so if the function returns
true
it will use the route, even if the RegExp constraint also passes.Remix API
In Remix, this can be defined as a module export.
Here's where accessing the request can be useful, since a Remix application could also check the user session, headers, or other parts of the URL.
Subdomain constraint
Another use case for constraints is to allow subdomains to match as params, let's say we want to have a route that matches
https://:username.example.com
.This is specially useful for multi-tenant applications, where each tenant has its own subdomain. But it will require extending the router to also know how to parse the hostname.
In this case, the router should parse the hostname and extract the subdomain, then it should use the subdomain as param value, removing it from the pathname.
Basically if there's a subdomain it will match this route and any nested route. As an example, let's say we have the following routes.
routes/($tenantId).tsx
routes/($tenantId)._index.tsx
routes/($tenantId).dashboard.tsx
routes/_index.tsx
routes/$article.tsx
If the user visits
company.tld/123
then the router will find two possible matches, onroutes/$article.tsx
androutes/($tenantId).tsx
(including the nested index).Here the constraint will tell the router that
routes/($tenantId).tsx
should only match for a subdomain, so it will discard it and useroutes/$article.tsx
to render the UI.If the user visits
acme.company.tld/
then the router will also find two possible matches onroutes/($tenantId).tsx
androutes/_index.tsx
, because the$tenantId
is optional.Here the constraint will tell the router that the tenantId param comes from the subdomain (
acme
) and it's valid, so it will useroutes/($tenantId).tsx
to render the UI instead ofroutes/_index.tsx
.Something to consider would be what happens with nested subdomains, e.g.
foo.bar.company.tld
, in that case the default handling could be to usefoo.bar
as the tenantId, but it could also be possible to add a constraint to the nested route to tell the router to usefoo
as the tenantId.Routes with a subdomain constraint must always be on the first level of routes, they can't nested inside another route that adds segments to the URL, this means this is valid:
routes/_layout.tsx
routes/_layout.($tenantId).tsx
// _layout is a pathless layout route hereBut this is not:
routes/app.tsx
routes/app.($tenantId).tsx
Prior Art
Beta Was this translation helpful? Give feedback.
All reactions