React Routing without Router

Rick Glascock
3 min readJan 10, 2021
Puxi, Shanghai — photo by Rick Glascock

React Router is the most popular package for setting up client side navigation within your single page app, and will be even better once Version 6 is officially released (smaller footprint, easily nested routes, and more!). But React Router isn’t magic, but rather built on a browser’s native window object’s APIs.

The Window interface is the container for all of the browser’s inner workings — DOM, JavaScript, global variables. It’s the mother ship.

The Window object represents the currently opened tab in a browser (or sometimes the entire browser window). If you type ‘window’ into your browser’s developer console, it should return the current Window object and all of its properties.

It’s important to understand the difference between server side and client side routing. With server side routing, when an anchor link to a URL is clicked, the browser will rebuild the window object from scratch as it loads the new page (the same happens when you reload the browser). This means that any information that had been previously stored (local state, functions) will be lost. By contrast, in a client side browsing environment (typical in a single page app), clicking a link that navigates to a new URL (within the root domain of the app) will change the URL in the address bar and load the new ‘page’, but will not cause the Window object to rebuild, thereby maintaining a sense of persistence while navigating.

How can we accomplish this in React without reloading the actual browser?

  1. We need get and store the current path in state.
  2. We need to update the browser’s history and address bar after ‘navigation’.
  3. We need to set up logic to conditionally show components (our pages) depending on the path that is displayed in the address bar or linked to.

Task 1:
The window.location API provides, among other things, a .pathname method that returns the path after the domain:
If the URL is: https://medium.com/p/e8db7052a1e/edit, window.location.pathname will return /p/e8db7052a1e/edit
We can use this to get and save the ‘home’ path into state (redux store or component state).

Task 2:
The window.history API keeps track of where we’ve been. history.back() and history.forward() act just like the back and forward buttons in the browser (and will refresh). By contrast, history.pushState() will add the url to history stack and will change the address bar without refreshing the browser. You can see the effect by calling the each method in the browser’s console.
history.pushState takes three arguments, the final one being the actual url string. The first argument, state, can be used to pass data (which can be retrieved with history.state. The second argument sets the title (although as of yet this doesn’t work). Upshot, you need to include something for the first two arguments, even if not needed:
history.pushState({}, ‘’, ‘my_URL’)

Note that the window.location API can accomplish almost the same effect:
location = ‘new_URL’

Task 3: We now set up conditional rendering within the main page based on the ‘path’ value saved in state to show the ‘page’ (component) corresponding to the url. We never actually refresh the main page!

That’s a very basic idea of how react router functions.

--

--

Rick Glascock

After years of teaching music in Austin, Shanghai and Yangon, I’m making a career change to my other passion, software development.