React Routing Reimagined

Graham Mendick
3 min readJun 15, 2020

--

Imagine there’s a parallel universe that’s identical to ours in every way apart from one thing. Route parameters haven’t been invented yet. Urls hold all their data in the query string. What does a React router look like in such a universe?

Let’s continue the thought experiment by looking at an example web app from this universe. The app has a single screen that displays a list of movies. To help the user find the film they’re after there’s a category filter and a pager. Because route parameters don’t exist, the category and page number appear in the query string. For example, the Url for the third page of comedy films is ‘movies?category=comedy&page=3’.

There are no hard-coded Urls in this new universe. There’s no point building a query string by hand when the Link component can do it instead. The component accepts the film category and page number via its data prop. It loops through the data and strings the parameters together, making sure they are Url-encoded.

<Link to=”movies” data={{ category: 'comedy', page: 3 }} />

There’s no nested router configuration in this universe either. No route parameters means no nested routes. The configuration is a flat array that maps routes to components. The router decides which component to render by checking its configuration against the incoming route. The film app maps the ‘movies’ route to a Movies component that renders the screen.

const config = [
{ key: 'movies', screen: Movies }
];

The waterfall problem doesn’t exist in this universe. Nobody has to worry about nested components triggering data fetches. The flat configuration means the router matches all the Url data upfront. The app can start fetching all screen data immediately, without waiting for rendering to begin.

In the new universe, this same router powers the file-based routing in Next.js. The flat configuration means there’s always a one-to-one mapping between screens and files. Nobody has to nest folders in the pages directory to satisfy their routing requirements.

We’ll return to our universe now, bringing the router and movie app with us. The plan is to update the router to support route parameters. For example, we want the Url for the third page of comedies to be ‘movies/comedy/3’ instead of ‘movies?category=comedy&page=3’. But we don’t want to go back to nested configuration and hard-coded Urls. Can we have the best of both universes?

Let’s start by adding a route to the movies configuration entry. It has route parameters for the category and page number. We’ll update the Link component to check for route parameters when building the Url. We’ll also update the router to match incoming Urls against the route parameter patterns.

const config = [
{ key: 'movies', route: 'movies/{category}/{page}', ... }
];

It’s a good start and it works when the user supplies both a category and page number. But when the app loads, the Url is just ‘movies’ and, when the user selects the film category, the Url is ‘movies/comedy’. The router can’t cope with either of these Urls because the route doesn’t match. There are three valid routes for this screen but our configuration only allows for one.

We must change our configuration to accept multiple routes without introducing nesting. We could accept an array that contains the three valid routes. But it’s repetitive and unclear.

[ 'movies', 'movies/{category}', 'movies/{category}/{page}' ]

Instead, we’ll introduce the ‘+’ syntax. It allows us to express multiple routes in a single string. The router sees the ‘+’ and registers three different routes for the one screen.

'movies+/{category}+/{page}'

The movie app now runs in our universe with route parameters. But this wasn’t just a thought experiment. The router from the parallel universe that we brought back and enhanced already exists. It’s called the Navigation router. Try out the movie app and see for yourself.

Movie app

--

--