src/router/) rather than a third-party routing library. Navigation is driven entirely by window.location.hash.
How the router works
TheRouter component (src/router/Router/Router.js) listens for hashchange events. On every change it:
- Parses the hash as a URL path plus query string.
- Iterates through
viewsConfigto find the first matching regexp (routeConfigForPath). - Extracts URL parameters by applying the regexp capture groups (
urlParamsForPath). - Updates the view stack — views at a lower layer index are preserved; views above the matched layer are cleared.
- Passes
urlParamsandqueryParamsas props to the route component.
onPathNotMatch is called and the returned component (the NotFound page) is appended above the existing stack.
Route focus
Each view is wrapped in aRouteFocusedProvider whose value is true only for the topmost (last) view. Components use the useRouteFocused() hook to check whether they are currently in focus.
useModelState subscribes to core state updates only when routeFocused is true. This prevents background views from consuming resources while a modal or higher-level route is active.
viewsConfig layers
Routes are grouped into five ordered layers insrc/App/routerViewsConfig.js. A layer corresponds to a visual depth in the UI: navigating to a route in layer 2 preserves the layer 1 view behind it.
Layer 0 — Board (background)
Layer 0 — Board (background)
The persistent background view. Always rendered unless another full-screen route replaces it.
| Route | Component |
|---|---|
| Board | routes.Board |
Layer 1 — Main navigation routes
Layer 1 — Main navigation routes
Layer 2 — Meta details
Layer 2 — Meta details
Overlays the navigation layer. Rendered on top of the current layer 1 view.
| Route | Component |
|---|---|
| Meta details | routes.MetaDetails |
Layer 3 — Overlay utilities
Layer 3 — Overlay utilities
Full-screen utility views.
| Route | Component |
|---|---|
| Addons | routes.Addons |
| Settings | routes.Settings |
Layer 4 — Player (topmost)
Layer 4 — Player (topmost)
The player renders above everything else.
| Route | Component |
|---|---|
| Player | routes.Player |
URL patterns
All routes are defined as regular expressions insrc/common/routesRegexp.js. The router matches hashes of the form #<path>.
- Board
- Intro
- Discover
- Library
- Calendar
- Continue watching
- Search
- Meta details
- Addons
- Settings
- Player
URL parameter extraction
urlParamsForPath (src/router/Router/urlParamsForPath.js) maps regexp capture groups to named parameters:
urlParams object always includes path (the raw pathname). Missing capture groups are null.
Modal routes
Modals are not separate routes — they are rendered inside the active route using theModal component (src/router/Modal/Modal.js). The Modal component portals its children into a container provided by ModalsContainerContext, which is set up per route by the Route component.
autoFocus— moves keyboard focus into the modal when it opens.disabled— disables focus lock (for non-blocking overlays).- Focus is scoped to the modal via
react-focus-lock.
Route component
Each view in the stack is wrapped by theRoute component (src/router/Route/Route.js):
ModalsContainerProvider creates the DOM node that Modal portals render into, scoped to this route instance.
Programmatic navigation
Navigate by writing towindow.location:
KeyboardShortcuts.js use this same mechanism:
| Key | Destination |
|---|---|
0 | #/search |
1 | #/ |
2 | #/discover |
3 | #/library |
4 | #/calendar |
5 | #/addons |
6 | #/settings |
Backspace | history.back() |
All navigation is hash-based. The path before the
# never changes, which means the app can be served from any path without server-side routing configuration.