Routing Mechanisms in Stremio Web: Custom Hash-Based Router Implementation

Stremio Web implements a lightweight, hash-based client-side router that matches URL hash fragments against regular expressions to extract parameters and render React views without relying on external routing libraries like react-router.

The Stremio Web application (Stremio/stremio-web) manages navigation through a bespoke routing architecture built entirely within its src/router directory. This system parses window.location.hash to determine active views, matches paths against configured regular expressions, and manages view state through React context providers. Understanding these routing mechanisms is essential for developers customizing the Stremio interface or debugging navigation flows.

Hash-Based Navigation and the Router Component

At the core of Stremio Web's routing lies the Router component defined in src/router/Router/Router.js. This component registers a hashchange event listener on the global window object within a useLayoutEffect hook (lines 73‑78), enabling the application to respond to hash fragment changes without page reloads. When the hash changes, the onLocationHashChange handler (lines 21‑23) parses the pathname and instantiates a URLSearchParams object from the query string portion of the hash.

Unlike traditional HTML5 History API routers, this hash-based approach requires no server-side configuration to handle deep links. The router maintains an internal views state array that tracks the currently matched view configurations, updating this state only when routeConfigForPath successfully identifies a matching route pattern.

Route Configuration and Pattern Matching

Routes in Stremio Web are defined through a viewsConfig prop passed to the Router component, structured as an array of view levels each containing routeConfig objects. Each configuration object specifies three properties: a regexp for path matching, an array of urlParamsNames corresponding to regex capture groups, and a component reference to render upon match. The PropTypes definition in src/router/Router/Router.js (lines 99‑105) enforces this contract, ensuring valid route configurations.

The actual matching logic resides in src/router/Router/routeConfigForPath.js. This utility iterates through the viewsConfig array and returns the first routeConfig whose regexp matches the current pathname. If no match exists, the router invokes the optional onPathNotMatch callback (lines 24‑38), allowing the application to render fallback content or handle 404 scenarios gracefully.

URL Parameter Extraction with urlParamsForPath

When a route matches, Stremio Web extracts dynamic parameters using the urlParamsForPath function from src/router/Router/urlParamsForPath.js. This function maps the RegExp match result's captured groups into a structured object using the urlParamsNames defined in the route configuration. The resulting urlParams object always contains a path property representing the matched string, alongside key-value pairs for each named parameter.

For example, a route configured with regexp: /^\/movie\/([^/]+)$/ and urlParamsNames: ['id'] produces urlParams containing { path: '/movie/123', id: '123' }. Combined with the queryParams URLSearchParams instance, view components receive complete access to both path segments and query string data.

Stremio Web's router provides fine-grained control over navigation through the onRouteChange callback prop. Before committing to a new view state, the router calls this function with the proposed route configuration, extracted urlParams, and queryParams (lines 45‑48). If onRouteChange returns a truthy value, the router aborts the standard view update, enabling custom navigation guards, analytics tracking, or conditional redirects.

This interception mechanism operates within the onLocationHashChange handler alongside the onPathNotMatch fallback. Developer-defined callbacks can inspect the proposed navigation and either allow the router to update its internal views array or handle the transition through alternative logic.

View Rendering and Focus Management

Once the router determines the active view configuration, it renders the corresponding component wrapped in a RouteFocusedProvider. This provider, exported from src/router/RouteFocusedContext/RouteFocusedContext.js, supplies a routeFocused boolean value indicating whether the view currently holds application focus. This context is critical for managing input handling, animation states, and media playback in stacked view architectures.

The public API surface in src/router/index.js exposes the Router component, Modal component for overlay dialogs, and the useRouteFocused hook for consuming focus state within child components. The Modal component integrates with the view stack without manipulating the underlying hash state, enabling modal interactions that preserve the current route context.

Practical Implementation Examples

The following examples demonstrate how to configure and utilize Stremio Web's routing mechanisms:

// Define route configurations with regex patterns and parameter names
const viewsConfig = [
  [
    {
      regexp: /^\/$/,
      urlParamsNames: [],
      component: Home,
    },
    {
      regexp: /^\/movie\/([^/]+)$/,
      urlParamsNames: ['id'],
      component: Movie,
    },
  ],
];
// Implement the Router component with navigation callbacks
function App() {
  return (
    <Router
      viewsConfig={viewsConfig}
      onPathNotMatch={() => <NotFound />}
      onRouteChange={(route, urlParams, queryParams) => {
        console.log('Navigating to', route, urlParams, queryParams);
        // Return true to prevent the router from updating views
        return false;
      }}
    />
  );
}
// Access extracted parameters within a view component
function Movie({ urlParams, queryParams }) {
  const movieId = urlParams.id; // Extracted from "#/movie/123"
  const startTime = queryParams.get('t'); // Value of "?t=5" if present
  
  return (
    <div>
      <h1>Movie {movieId}</h1>
      {startTime && <p>Resume from {startTime}</p>}
    </div>
  );
}
// Fallback component for unmatched paths
function NotFound() {
  return <div>Content not available</div>;
}

Summary

  • Hash-based architecture: Stremio Web uses window.location.hash changes and a hashchange listener in src/router/Router/Router.js rather than the HTML5 History API, enabling deep linking without server configuration.
  • Regex-driven matching: Routes define regexp patterns in viewsConfig, processed by routeConfigForPath to determine active views based on current hash paths.
  • Structured parameter extraction: The urlParamsForPath function converts regex capture groups into named parameters accessible via urlParams props in view components.
  • Navigation interception: The onRouteChange callback allows applications to intercept and conditionally prevent route updates before the router modifies its internal state.
  • Focus-aware rendering: RouteFocusedContext provides focus state to nested components through RouteFocusedProvider, enabling sophisticated UI behaviors in multi-view layouts.
  • Modal support: The Modal component exported from src/router/index.js renders overlay content without disrupting the underlying hash-based route state.

Frequently Asked Questions

How does Stremio Web handle 404 errors or unmatched routes?

When no configured route matches the current hash path, the routeConfigForPath function returns null, triggering the optional onPathNotMatch callback in src/router/Router/Router.js (lines 24‑38). Developers can supply a fallback component through this callback to render custom error pages or implement redirect logic when users navigate to undefined paths.

Can I use query parameters in Stremio Web routes?

Yes, the router automatically parses the query string portion of the hash into a URLSearchParams instance passed as queryParams to view components. You can access these values using standard URLSearchParams methods like .get('key') within any component rendered by the router, regardless of the specific route configuration.

What is the purpose of the RouteFocusedContext in Stremio Web?

RouteFocusedContext determines whether a rendered view currently holds application focus. Wrapped around each route via RouteFocusedProvider in src/router/RouteFocusedContext/RouteFocusedContext.js, this context enables components to adjust behavior based on visibility—such as pausing media playback when a view loses focus or managing keyboard navigation scope in stacked interface layers.

How do I prevent a route from changing in Stremio Web?

Return true from the onRouteChange callback prop passed to the Router component (lines 45‑48). This function receives the proposed route configuration, urlParams, and queryParams before the router updates its internal views state. Returning a truthy value aborts the navigation, allowing for conditional routing logic such as authentication checks or unsaved change warnings.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →