Note: This code is now redundant since Next.js announced Automatic Resolving of "href". However, this is still a great source for education and in some rare cases may still be useful - such as if a codebase is stuck on an older version of Next!
If you’ve used Next.js for any length of time, you know that pages and page routes are defined by the file structure of the app. This can simplify the need to manually declare routes and makes it easier to quickly get a simple app up and running!
However, this creates a pain-point when it’s time to work with lots of dynamic routes.
As a note, this post assumes you’re already familiar with how Next handles dynamic routes within it’s file structure. If not, you can always check out the documentation to learn about the specifics.
The main point of contention comes from how you have to define a route twice when you want to navigate to a page that’s generated by a dynamic route. If you decide to use either Next’s
Link element or the very handy
useRouter hook, you’ll have to define the link like so:
As you can see, we had to write the URL string in
as, but we also had to define the file-structure path in
href. In most cases this is fine because typically you’ll know where you’re sending a user and you can just write the
href line without a problem. However, this becomes a concern when you need to store a dynamic URL in a cookie or URL parameter because you’ll have to store both the URL and the file-structure path. Depending on your code structure and how you store the values, it may even be a potential security concern as the file-structure line will reveal your routing logic! Not to mention that this style of routing is extra cognitive overheard, especially when you are dealing with nested dynamic routes and catch-all routes in every link you write.
The solution is to eliminate the need to manually define
href on every link by automatically deriving it based on the provided URL. The issue is that you can’t directly derive a file-structure path from the URL string! We’ll need to implement another tool. Thus comes path-to-regexp, a tool that allows us to convert path-strings to a regular expression and then check if a provided URL string matches the regexp!
Let’s pause for a moment and clarify some terminology we’re using. We’re now working with 2 different types of strings that relate to the logical mapping of URL routes.
file-structure path is the non-standard URL routing method used by Next.js to map a URL to their file-based routes.
Some examples are:
path-string is the standard URL routing method used in many websites, servers, etc. The same examples as above written in path-strings looks like this:
There’s a problem with using the
path-to-regexp library. As mentioned above, Next’s file-structure paths aren’t standard, and we’ll have to add an extra step that converts Next file-structure strings into a standard path-string. The simplest way to do this is creating a file of routes with all of our file-structure routes defined along with their path-string counterparts. The path-to-regexp github page has some basic instructions on writing standard path-strings. In short, if you’ve ever used react-router then you’ve already worked with path-strings.
Here’s an example routes file:
Here we’ve declared an array that contains all of our links in our application. Each item is another array where the first value is Next’s file-structure path, and the second value is it’s path-string counterpart. With these defined we can easily match back-and-forth between the two!
It should be noted that the ordering of elements matters! If a URL would potentially match multiple items, we’re going to select the first one that it matches. You may also notice that our routes file isn’t very DRY. More on that later.
Next we need to create a function that will take a URL, check if it matches any of our listed string-paths, and then return the corresponding file-structure string. Don’t worry, it’s not as complicated as it sounds:
Technically, we’re done! All that’s left is to use our new
deriveNextPath function anywhere we want to automatically generate our
href. A great example is to create a wrapper for
Link and then use it:
href value will be automatically generated for us as long as our
routes.js file stays up to date.
Want to try a few optimization challenges?
- As I mentioned above, the
routes.js file isn’t DRY for static routes. What change needs to be made to allow you to declare static routes only once within that file?
- In almost all cases in our
routes.js file, we’ll declare dynamic/catch-all/etc routes to match between file-structure paths and path-strings in the exact same way, so how can we take this code a step further and automatically generate our path-strings based on the file-structure paths?