The 60-Line Hash Router That Explains Every Modern Front-End Framework
Modern frameworks abstract routing behind declarative APIs, but the underlying mechanism — a lookup table, a URL-change event, and a DOM swap — has not changed. Knowing the 60-line core prevents treating React Router or Vue Router as magic and makes debugging route transitions, `this`-binding bugs, and lazy-loading splits far more straightforward.
Traditional multi-page applications reload the entire document on every navigation, causing white flashes, redundant bandwidth consumption, and total state loss. The pivot to single-page applications began when developers realized that hash fragments — originally meant for in-page anchor links — never trigger a page refresh, making them a natural vehicle for client-side routing. A minimal HashRouter class, built with a routing table object, a `hashchange` event listener, and a `load()` dispatcher, replaces only the target DOM region while keeping shared chrome intact. The same pattern — map a path to a handler, listen for URL changes, update a view region — persists in today's History API-based routers, lazy-loaded component trees, and dynamic route matching. The piece also unpacks the `bind`, `call`, and `apply` mechanics that make the event listener's `this` binding work, a detail that trips up developers reading framework internals.
The jump from anchor links to client-side routing was not a framework invention but a creative reuse of an existing browser behavior — `hashchange` — that was never intended for application-level navigation.
Frameworks add enormous value in scalability and developer experience, but the distance between a 60-line HashRouter and a production router is mostly engineering robustness, not conceptual novelty.
JavaScript's `this`-binding quirks are not academic edge cases; they are load-bearing details in the simplest possible router, and misunderstanding them breaks the entire dispatch chain.