Single-page apps (or SPAs as they’re sometimes called) serve all of the code for an entire multi-UI app from a single index.html file.
They use JavaScript to handle URL routing with real URLs. For this to work, you need to:

Configure the server to point all paths on a domain back to the root index.html file. For example, todolist.com and todolist.com/lists should both point to the same file.
Suppress the default behavior when someone clicks a link that points to another page in the app.
Use more JavaScript—history.pushState()—to update the URL without triggering a page reload.
Match the URL against a map of routes, and serve the right content based on it.
If your URL has variable information in it (like a todolist ID, for example), parse that data out of the URL.
Detect when someone clicks the browser’s back button/forward button, and update the URL and UI.
Update the title element on the page.
Use even more JavaScript to dynamically focus the content area when the content changes (for screen-reader users).

(Shoutout to Ashley Bischoff for those last two!)
You end up recreating with JavaScript a lot of the features the browser gives you out-of-the-box.
This becomes more code to maintain, more complexity to manage, and more things to break. It makes the whole app more fragile and bug-prone than it has to be.
I’m going to share some alternatives that I prefer.

Go Make Things

Like – it seems – Chris Ferdinandi, I’ve got nothing against Single Page Applications in their place.
My biggest concern with SPAs is that they’re routinely seen as an inevitable progression of web development: that is, that an increasing number of web developers have been brainwashed into thinking that they’re intrinsically superior to traditional multi-page websites. As Adam Silver observed the other year, using your heavyweight Javascript framework to Ajaxify your page loads does make the application feel faster… but only because the download and processing time of the heavyweight Javascript framework made it feel slow in the first place! The net result: web bloat, penalising of mobile users, and brittle applications with many failure points.
Whenever I see a new front-end framework sing the praises of its routing engine I wonder how we got to this point. After all: the Web’s had a routing engine since 1990, and most efforts to reinvent it invariably make it worse: less-accessible, less-archivable, less-sharable, less-discoverable, less-reliable, or several of these.