Single-page apps need to change the view without hitting the server. The oldest, most bulletproof way is the URL hash: everything after # is client-only — the browser never sends it and never reloads — yet changing it updates the address bar and the back button. A hash router listens for hashchange, matches the current hash against your route patterns, pulls out params like :id, and calls the right handler.
Implement hashRouter() with on, subscribe, start, navigate, and stop. See MDN: hashchange.
function hashRouter() {
return { on, subscribe, start, navigate, stop };
}
const router = hashRouter()
.on('/users/:id', (params) => render(User, params)) // params.id
.on('/', () => render(Home));
router.subscribe(({ path, matched }) => console.log(path, matched));
router.start(); // resolves the current hash now
router.navigate('/users/42'); // sets location.hash = '#/users/42' -> User handler
# — #/users/42 → /users/42; an empty hash means /.:name segments are params — /users/:id matched against /users/42 yields { id: '42' }; decode the values.{ path, params, matched }. stop() must remove the hashchange listener.Unlock the solution & editor
Runnable editor + tests
Solve in the browser with instant Jest feedback.
Detailed solutions
Walkthroughs, edge cases, and complexity notes.
Multi-framework variants
React, Vue, Vanilla, Angular — same question, different stacks.