- Location
- pathname
- state
- search/query
- hash
- Path
- Dynamic Segment
- Wild Card
- Regex
- Matching
- URI
- Parameters
- State
- Query
- History
- Listener
- State
- History Stack (push/replace/index)
- Routes
- Path
- Data
- Validation
- Links
- href
- state
- active status
- File System API
When a user visits a webpage, the location is the the first (and probably the only) data the app has to generate a UI. The location is matched against a set of routes, a route is selected, (maybe) data is loaded, and finally the UI is rendered.
After the initial render on the client, a history listener is set up. When the history's location changes--either through the user clicking links or the programmer redirecting with code--the new location is matched against the routes, (maybe) data is fetched, and the app is updated to the new page.
Some implementations will save the data loading of a route for after the new page is rendered (and display some spinners). There are a handful of tradeoffs regarding data loading on page transitions, so we'll save that conversation for later.
Before we can talk much about anything else, we should address the three server side approaches used to deliver the application to the browser.
- "Single Page App"
- Static File Generation
- Serverside Pre-Rendering
A traditional website handles URLs on the server. This can be as simple as an html file for each page of your site: /index.html, /contact.html etc. But in order for a client side router to work, every URL the server receives needs to run the same application. Or, in other words, every URL needs to serve the same html file.
This is why they've been called "Single Page Applications". There's really only a single index.html that handles every URL in the app, and then a router decides what UI to render with JavaScript in the browser.
The body of the html file is typically blank, with a single element for JavaScript (and your client side router) to mount an application into.
<html lang="en">
<meta charset="utf-8" />
<title>Bare Bones SPA</title>
<body>
<div id="root"></div>
<script src="the/app.js"></script>
</body>
</html>No matter what the URL is, when if this file is served, then the router inside of app.js can read the URL and determine what page to display.
Here's a bare-bones express server to do just that:
const express = require('express');
const app = express();
// serve all static files that exist as the file (CSS, JS, etc.)
app.use(express.static('public'));
// serve every other request to your root index html file
app.get('*', res => {
res.sendFile(path.join(__dirname, 'index.html'));
});
app.listen(5000);Now if the user visits /invoices/123 they will get the index.html file and the client side router can decide what to render.
This is how create-react-app plus a router like React Router works.
In the early days of web development, before client side routing and even before server side routing, we just had html files. We just put html files into the public facing directory of our web servers and we had a "route".
A silly homepage public directory could look like this:
├── about.htm
├── contact.htm
├── index.htm
└── pics
├── IMG_2342.JPEG
├── IMG_3532.JPEG
├── cat.htm
├── dog.htm
├── index.htm
├── me.htm
└── me.jpeg
This is why URLs look like a directory structure on your computer, because that's what it originally was!
Modern tools have continued to take this approach, but instead of writing separate html files for every page, you can create just the body of the page with JavaScript like its a Single Page app. At build time, the pages are compiled into full HTML files at the same paths where they were authored for the web server.
When a user visits, and an HTML file is loaded into the browser, the JavaScript app, along with a router, is loaded. Every navigation from now on acts exactly like a Single Page Application--no more round trips to the server for assets, just data.
It feels like the old days of just creating files to get a new "route" with all performance benefits of static file servers, except the transition from "plain html" to dynamic, interactive UI is seamless because it's the same code.
Gatsby.js works this way, and Next.js supports it (in addition to full server rendering).