New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[RFC] Dynamic Routes #7607
Comments
Poll: To express interest in optional parameters, please react with a "+1" this comment. Note: Optional parameters are already possible with this RFC, they just do not have an explicit syntax (see Caveats section). |
Poll: To express interest in catch-all parameters, please react with a "+1" this comment. Note: Please share your use case for catch-all parameters in this thread! We'd love to understand the problem-space more. |
This comment has been minimized.
This comment has been minimized.
On ricardo.ch, we use a locale prefix for each route which make routing a bit more complex. Example of valid routes:
Do your think such prefix parameters could be supported? At the moment, we use https://www.npmjs.com/package/next-routes Another thing: for the article page, we also support a slug before the id like |
@ValentinH the provided routes are all possible using the filesystem API -- given your provided routes:
This use case is addressed above:
Does this solution not meet your needs? We'd love to learn more about your specific requirements. |
Thanks a lot for the answer. I didn't thought about using Regarding the "named parameter in middle", I overlooked it because I thought having the slug being dynamic was different. However, you are completely right and this is addressed by the paragraph you mentioned. Striping the slug in the application code will be the way to go 🙂 |
Would something like this (parse params from .hidden .files/.folders) be possible?
or leave the $ so one could find their files :D but always use $folder to indicate a param?
|
I used to have this use-case for optional parameters in an app that worked with npm packages. These could optionally have a scope. There are routes like:
So basically, the scope parameter is optional, but it's also only a scope when it starts with In the end it was solved in <Switch>
<Route path={`/packages/`} exact component={PackagesOverview} />
<Route path={`/packages/:name(@[^/]+/[^/]+)`} component={PackageView} />
<Route path={`/packages/:name`} component={PackageView} />
</Switch> I'm not sure I see a solution for this use-case in this RFC. As for catch-all use cases, I'm thinking any deep linking into recursively nested data, like folder structures, treeviews, treemaps. |
My 2 cents: dollar signs in filenames are a bad idea because they're used by shells as a sigil. You're going to confuse people trying to run More broadly: like many people, I've tried to leverage the file system as a solution to this in the past. Ultimately, I think the file system is never going to offer the full expressiveness you're looking for. For example, declarative routers usually let you specify a validation pattern for a dynamic parameter. In that case, part of the schema lives on the file system, and another part in the code. Separation of concerns is a good thing, but in this case, it's a technical limitation more than anything else. |
Like @ValentinH we use the $locale var, but it's optional. Should we use /page.ts and /page/$locale/page.ts? Because we can use a "default" locale or a predefined locale ( user settings ), in those cases we don't use the $locale param. But we have more use cases: /car/search/$optional-filter-1/$optional-filter-2/$optional-filter-3 Where optional-filter-1: color-red, optional-filter-2: brand-ford, etc... And for optional params, something like /$required-param/ and /$$optional-param/? |
Awesome that this is coming up on the roadmap! I have to chime in supporting @timdp though. When you can't even I'm proposing two alternatives that I feel gives the right associations and should work in shells:
|
Another option would be to use curly braces (unless they are reserved characters on some file systems). This parameter syntax is also "prior art" and is used by many other routers:
This would allow to have parameters in the middle of the route segment and multiple parameters per segment as it's clear where the parameter starts and where it ends, e.g. |
So excited that dynamic routes is coming to Next.js! Regarding the syntax for named parameters, this is something that has been discussed on Spectrum: https://spectrum.chat/next-js/general/rfc-move-parameterized-routing-to-the-file-system~ce289c5e-ff66-4a5b-8e49-08548adfa9c7. It might be worth using that as input for the discussion here. Personally, I like how Sapper is doing it using Regarding the usage of |
Curly braces are unfortunately used by Bash to group commands. |
I agree with @stephan281094 regarding usage of |
Dynamic routing is an extremely useful feature, so it's really awesome you guys have looked into it and came up with a solution, huge props! While on this topic, wildcard routes would also be a worthy addition to the proposal. You did mention catch-all parameters as something to investigate in the future, but it doesn't cover cases where you might want to do something like |
Is it possible to use |
Due to |
Apparently |
So a list of characters to avoid would be:
Anything else? |
This wouldn't eliminate our need to have a centralised route file for a couple of reasons:
We also generate our pages folder for these reasons:
Essentially our pattern is to use our centralised route configuration to generate our pages folder, which contains files which do nothing more than import/export modules from elsewhere in the codebase. To that end, my focus is more on whether this proposal can work simply as an enhanced output format for our existing page generation process, so that we can at least get the benefit of not needing a custom server. I've gone over some of my use cases elsewhere: https://gist.github.com/AndrewIngram/8d4c4ccd9bd10415a375caacade9f5ca The main thing i'm not seeing is supporting implicit parameters that aren't expressed in the file-system, for example URL overrides. Let's say we have a URL like this:
Where in current Next.js terms, we'd want it to map to a product page with a number of query parameters, e.g Similarly, on our website most countries "sites" are scoped by a top-level segment eg The other downside, is that because in our case, the same 'page' can exist at multiple mount points in the URL architecture, we're going to end up with a greater number of bundles (i.e. several duplicate entry points) than we actually need in practice. On the whole this proposal seems like it's going to work well for most common use cases, but it doesn't obviate the need for a route config or custom server in all cases. But assuming this doesn't replace my ability to use the framework the way I do today, I don't have any real objection to this being the preferred happy-path API. |
I support the |
I'm in favor of the |
I would use Appreciate the work on this. Been wanting it for a while! ❤️ |
Amazing! 🎉🎉🎉 My only issue here is that I believe we could just write |
|
This looks really cool. It does seem like this will create a significant number of single file directories (like I also don't love that the filename for rending a blog post would be Perhaps combine with a // pages/blog.js
import {useRouter, @customRoute} from 'next/router'
@customRoute('/blog/:id')
function BlogPost() {
const router = useRouter()
// `blogId` will be `'how-to-use-dynamic-routes'` when rendering
// `/blog/how-to-use-dynamic-routes`
const blogId = router.query.id
return <main>This is blog post {blogId}.</main>
}
export default BlogPost This seems to provide a cleaner solution for the proposed catch-all parameters as well. |
Decorators can't be applied to functions (maybe this changed since I last read it?) and the proposal is probably a long way away anyway |
@merelinguist em, Jest use |
@yufengwang Perhaps, but I’d prefer a single character if possible. Ultimately, I think the best solution would be:
|
Agreed with a single character but I'd prefer to have a zero configuration. and my guess is that many people will go through all issues even if you describe them in a documentation |
Note also
|
Just an idea; what about using a suffix? For example These work in zsh and bash without the need to escape, so far:
|
Ooh. Not a suffix but a way to denote trailing URL params. So
This could solve the deeply nested single file directories I was objecting to above, and keep the entire routing definition file system based. |
It doesn't look as fancy, but how about something along the lines of:
a prefix |
How about Like |
Using Any character that doesn't match Some characters aren't safe inline, either. See zsh's docs about substitutions |
Also I think we shouldn't strive for whether it looks fancy, but rather be intuitive, readable and easy to communicate. |
|
imho: this feels like a temporary solution to the greater problem. While having routes based on file structure is very nice to have to begin with, it doesn't scale well when you have hundreds of routes, params, etc. Having a routes config file (maybe have a routes.js file in each directory) is a better long term solution. I am personally drawn to nextjs because of the features out-of-the-box (SSR, speed, etc) it provides, not the ease of creating routes from files. |
@mmahalwy you hit the nail on the head. Next.js already generates a routes config (based on the filesystem). I believe that making this config more explicit and/or allowing the user to "eject" it if they wish would be the most seamless solution here |
@mmahalwy @scf4 FWIW, a significant justification for filesystem routes is to remove the need to have a centralised file. In fact, one could easily argue that the entirety of Next.js's API for links and routing is designed around this constraint. The problem with a route config is that you end up having to ship it to the client, which can mean quite a hefty code bundle if you have routes numbering from hundreds to thousands. However, there are quite a few common use cases that (as far as I've been able to tell, from discussing this issue with @timneutkens numerous times over the past few months) can't really be solved without a centralised config. I listed some of them in my earlier comment, but there are more. The simplest one is having a CMS-driven blog where authors can create links to pages on the site. They'll just be creating links with a plain old URL, with no knowledge of what the underlying page module is. With a centralised route config, it's pretty easy to reverse match a URL and work out which page to load (my own library, next-route-resolver is designed to support this use cases, and all the others i've come up with). I don't see how I can make the site I'm working on work without a route config, so my focus has just been on finding ways to keep the route config within filesize tolerances. For other people, filesystem routing may be more than sufficient. I don't think routing is a problem where there's a single solution that solves everything, it's all about balancing trade-offs. So as I mentioned before, as far as this proposal is concerned, it seems fine as long as it's sold as solving the routing problem entirely, because that would be a little misleading :) |
@AndrewIngram I understand where you are coming from but this limitation is limiting the power that nextjs has. Nextjs offers so much out of the box that it should be a no-brainer for any new project or company to use it. The challenge though is it's hard opinion on routing that make it unejectable in the future (and as a large company, you're always considering the exit strategy should projects lose interest or maintenance). |
@mmahalwy I think you misunderstood my point. I'm in agreement with you, I don't think file-system routing is sufficient to call the routing problem solved, and would be disappointed if it was presented as such. I do think it offers an improvement for a particular set of use cases, but I also think there should also be some kind of route manifest format for those willing to opt-in to a different set of trade-offs (e.g. you and me). |
For those wishing for a centralized or advanced routing config, isn't it well handled by using the custom server and/or external packages? What are you hoping gets added here? It all seems off topic from this RFC. I don't think anyone, including the OP, has suggested this is the end-all solution for routing. This just improves on the filesystem based routing. |
I've been using the dynamic routes for a mini project for the last few weeks (using I just started using Is that a bug, or intentional (i.e. some change to |
One of the goals here is to avoid the need to create a custom server, if only to make it easier to use with services like Now (which currently requires all the dynamic routes to be part of its config).
There actually is some additional context here. This proposal has been a long time coming, and based on many of the discussions i've seen related to it (including ones i've been directly involved with), this was being hyped to some degree as removing the need for using these route management libraries like next-routes and my own. I don't think it's off-topic to highlight the use cases which aren't fulfilled by this RFC. Some of them might conceivably be fulfilled by some changes to the proposal, others might not. But either way, surely it's valuable to raise awareness of the limits of what's being proposed? |
FWIW we use |
@chrislloyd What are your experiences with creating and managing files using this format for paths/files in different environments, considering anyone is using zsh, or a tool that interprets these differently? Seen as the |
I have an idea on using It doesn't have any issue with prefix like above discussions, and it's familiar on how |
Windows doesn't allow question marks in file names, and both ? and ! have a special meaning in Bash. |
|
@remy |
Hi everyone! Thanks for the incredible response to this RFC. This RFC has been implemented and released as stable in Next.js 9. We're going to publish a new RFC in the future to address all the advanced feedback given here. We'll post an update here when it's available. |
Dynamic Routes
Background
Dynamic routing (also known as URL Slugs or Pretty/Clean URLs) has been a long-time requested feature of Next.js.
Current solutions involve placing a L7 proxy, custom server, or user-land middleware in-front of your application. None of these solutions offer a sufficiently ergonomic developer experience.
Additionally, users reaching for a custom server inadvertently opt-out of advanced framework-level features like per-page serverless functions.
Goals
/blog/:post
<Link />
route transitions when possibleProposal
Next.js should support named URL parameters that match an entire URL segment. These routes would be expressed via the filesystem:
[]
would be considered a named parameterquery
object (accessible fromgetInitialProps
orrouter
viawithRouter
) — these parameters can not be overridden by a query parameterTo help understand this proposal, let's examine the following file tree:
Next.js would produce the following routes, registered in the following order:
Usage Examples
These examples all assume a page with the filename
pages/blog/[id].js
:Navigating to the Page with
<Link />
The above example will transition to the
/blog/[id].js
page and provide the followingquery
object to the Router:Reading Named Parameters from Router
Note: you can also use
withRouter
.Reading Named Parameters in
getInitialProps
Caveats
Optional route parameters are not expressible through the filesystem.
You can emulate an optional route parameter by creating a stub page that exports the parameter version (or vice versa). This increases the visibility of your application's routes when inspecting the filesystem.
Named parameters cannot appear in the middle of a route name.
This means a page named
blog-[id].js
would be interpreted literally and not matched by/blog-1
. You can either restructure your page to be/blog/[id].js
or turn the entire URL Segment into a named parameter and handle strippingblog-
in your application's code.Alternatives
Denote URL Slugs with insert symbol here instead of
[]
There are very few symbols available for use to represent a named parameter on the filesystem. Unfortunately, the most recognized way of defining a named parameter (
:name
) is not a valid filename.While surveying prior art, the most common symbols used to denote a parameter were
_
,$
and[]
.We ruled out
_
because_
is typically indicative of an internal route that is not publicly routable (e.g._app
,_document
,/_src
,/_logs
).We also ruled out
$
because it is a sigil in bash for parameter expansion.Leverage
path-to-regexp
for comprehensive supportMost of the symbols required to express regex are not valid filenames. Additionally, complex regexes are sensitive to route ordering for prioritization. The filesystem cannot express order nor contain regex symbols.
In the future, we may allow
path-to-regexp
routes defined innext.config.js
or similar. This is currently out of scope for this proposal.Future Exploration
Catch-All Parameters
In the future, we may consider adding catch-all parameters. With what we know thus far, these parameters must be at the end of the URL and would potentially use
%
to denote a catch-all route (e.g.pages/website-builder/[customerName]/%.tsx
).The text was updated successfully, but these errors were encountered: