Routing with Next.js
Over the last year or two I have started most projects using Next, a framework that can be used to create React websites. While most aspects of the framework I find very helpful, some of it I find still needs some tuning. Particularly when it comes to deployment and routing. I will go over some recommended ways to use Next, and then touch on a practical issue I have faced in the past and how it was solved. Let's begin with the easy paths...
Hosting Dynamically
The easiest way to serve a Next website is by starting a Node server (using Next's built in commands) and point your browser to that server. I think of this way of serving the website as dynamic, because Next is able to build the HTML being served on every request, it does not send the same HTML for every route. Using this method gives access to all routing features within Next, like parameterized page routes. A parameterized page route contains changeable parts, like
/blog/some-post
or/blog/another-post
. In these two URLs, the blog page is served by the same code, but is referenced by a slightly different URL. To get this working with Next, one must programatically tell Next about this within the Node server coded that replies to every page request. Alternatively, one could serve these routes using queries, like/blog?post=some-post
and/blog?post=another-post
. In these query-based URLs, the same page is used to serve both requests; also, the Next Node server does not need to be modified from it's default settings. However, using a query in the URL is not so pretty, could result in poorer SEO, and we should be able to serve a nice URL without?
and=
in it. So, to do this in Next, it requires altering the Node server that Next provides (which is a documented and is a standard use-case). Because our pages are served dynamically, with parameterized routes our Next Node server is able to detect the URL pattern and serve the same page for pretty URLs.Hosting Statically
A statically hosted site always serves the same files for every request. The files were generated at some point in the past and will not change, ever. I prefer hosting websites statically, because it seems like less can break—the only code to worry about is the front-end code...whereas in dynamic hosting there are potential Node issues at play. Let's start by examining some pages in a statically hosted environment...First up, different pages, like
/about
and/help
pages. These pages serve completely different content, are at different URLs, and they do not change at all. With our blog pages example, much was different. The URLs are different and the content is different, but the page used to generate the content is the same. If going to blog pages like/blog?post=some-post
and/blog?post=another-post
, the browser will route to the same statically generated/blog
page, but different content will be rendered based on the query—this is still the happy path. But what happens when we try to use routes like/blog/some-post
and/blog/another-post
?Static Woes
In the dynamic hosting method the server matched the URL to
/blog/:id
and then, smartly, served the individual blog page based onid
in the URL. From the browser's perspective it was served a page with the correct content. From the Next Node server perspective, it receieved a parameterized route request and served a dynamic response, based on theid
.In a static hosting situation, Next will generate whatever pages you specify or a route per page (not including any parameterized routes). In some cases it is not possible to know all pages that need to be generated, particularly with parameterized routes. So, we can only generate a single blog page that somehow accepts an
id
to generate the correct content. The troubling part is when the browser requests a page that simply does not exist as a static page on the server, because it is a parameterized route. Currently, in Next, you would be able to navigate to a parameterized route from another Next page, but if you hit the refresh button, you will land on some sort of 404 page. Why? Because with static hosting, if the file does not exist, you get a 404 page. When hosting a static site that has parameterized page routes, two things must happen. Let's go over what that is...- The server must respond with some sort of gateway to the website when a parameterized page is requested, not an automatic 404 page.
- The browser, upon loading this gateway page, must then route to and display the correct parameterized page.
Currently, within Next, there is not a published way to do these two things.
A Custom Solution
I was stuck with this issue when trying to deploy this website in a static environment. Because I wanted to have multiple parameterized routes (
/thoughts/:id
and/projects/:id
), I figured it was best to only generate a singleindex.html
file and have the server point all requests to that file. The implications of this are that going to/about
,/help
,/thoughts/:id
, or/projects/:id
would all return the content of/index.html
. That/index.html
content would act as #1, the gateway to the website. With that complete, all that's left was #2, which is some code that the browser uses change routes (without the user knowing) to the correct page.At the time, I was working in the land of Amazon Web Services. The website files were uploaded to an AWS S3 bucket and all requests for files that did not exist were redirected to the
/index.html
file. I hoped Next would somehow provide some sort of catch-up or sync routing functionality, but it does not. To me, this is a bit troubling. To me, static hosting is fast, cheap, and quite common. For there not to be a way to do pretty URLs with parameterized routes in a static hosting environment is a woe indeed. Nevertheless, I created a function that could be executed within thecomponentDidMount
lifecycle of the Next specificpages/_app.js
file. The function would route to the real page the browser intended to go to, rather than the/index.html
content. I do not use this function anymore, but a version of it is published as parlor for anyone to check out a solution.Concluding Thoughts
- These days I use Now to dynamically host this website, but I can't say it is perfect either. Currently, the latest (Version 2) does not fully support parameterized routes.
- I am still a big fan of Next, but tackling this issue seems like something that could come out-of-the-box from a project with built-in routing. I understand the direction of Next is not geared towards static hosting, but offereing a static export option that does not work with parameterized routes is tough.
- Becoming familiar with publishing parlor on NPM was quite an experience on it's own.
Posted January 15th, 2019.