Best Practices for Developing with Stremio-Web: A Complete Guide

Stremio-web development follows a strict Component-Context-Service architecture that mandates dependency injection through the useServices hook, declarative routing via centralized configuration files, and strict isolation of core logic within the src/core/ directory.

Stremio-web is the modern React-based frontend for the Stremio streaming platform. Following the established patterns in the Stremio/stremio-web repository ensures your contributions remain maintainable, testable, and consistent with the existing architecture. This guide covers the essential conventions for service injection, routing, internationalization, and build tooling based on the actual source code implementation.

Follow the Component-Context-Service Pattern

The codebase enforces a clear separation of concerns across three layers. UI components should never directly import service clients; instead, they consume dependencies through the ServicesContext provided by ServicesProvider.js located at src/services/ServicesContext/ServicesProvider.js.

The context definition in src/services/ServicesContext/ServicesContext.js establishes the contract for all runtime services. When adding new API clients or utilities, register them in the services index and pass them through the provider rather than using module-level imports. This guarantees that the dependency graph remains explicit and facilitates mocking during testing.

Use the useServices Hook for Type-Safe Access

Always access injected services through the useServices hook defined in src/services/ServicesContext/useServices.js. This hook provides compile-time safety via TypeScript typings and ensures components receive the correct service instances from the context.

Never import the provider directly into presentational components. The hook abstraction allows the testing framework to substitute mock services easily without modifying component code. The hook implementation guarantees that services are available or throws a descriptive error if used outside the provider tree.

Configure Routes Declaratively in Centralized Files

The routing system relies on configuration objects rather than inline JSX routes. Define new URL patterns in src/router/Router/routeConfigForPath.js, which maps paths to lazy-loaded view components. The Router component at src/router/Router/Router.js consumes this configuration to handle navigation and code splitting automatically.

For URL parameter parsing, maintain the mapping logic in src/router/Router/urlParamsForPath.js. This separation prevents duplicate regex patterns scattered across components and ensures consistent parameter extraction. When adding a view, update only the route configuration and the lazy import statement—never implement manual URL parsing logic inside components.

Manage Modals Through ModalsContainerContext

Modal handling is centralized via ModalsContainerContext to ensure proper stacking and unmounting behavior. Implement modal providers using the pattern established in src/router/ModalsContainerContext/ModalsContainerProvider.js.

When a view requires modal functionality, wrap it with the appropriate context provider rather than managing local modal state. This approach guarantees that multiple modals stack correctly and that background scrolling locks properly across different view hierarchies.

Isolate Core Logic in the src/core/ Directory

The low-level Stremio engine integration lives under src/core/ and exposes its API through CoreProvider, instantiated in src/index.js. Keep all core-related business logic strictly within this directory to prevent leakage into UI components.

Components should interact with core functionality through the CoreContext defined in src/core/CoreContext.ts rather than instantiating core objects directly. This isolation ensures that the React layer remains agnostic of the underlying Stremio engine implementation details, making future core updates backward-compatible with the UI layer.

Initialize Internationalization at Bootstrap

The application loads translations through the stremio-translations bundle during startup. In src/index.js, the i18n initialization occurs before the React tree renders. All user-facing strings must be added through this mechanism to ensure synchronization across supported languages.

Avoid hard-coding English strings in components. Instead, use the translation keys provided by the bundle. This guarantees that community translations remain in sync and that the interface respects user locale preferences automatically.

Adhere to pnpm and Webpack Tooling Standards

The project uses pnpm exclusively for package management, as specified in package.json and the Dockerfile. Use pnpm install to populate dependencies and avoid mixing npm or yarn to prevent lock-file inconsistencies.

Webpack handles bundling with scripts defined in package.json. Use pnpm start for local development with hot reloading and pnpm run build for production optimization. The provided Dockerfile at the repository root ensures reproducible production deployments.

Implement Environment Safety Checks

Sensitive configuration values such as SENTRY_DSN are read from process.env only after runtime type validation (typeof … === 'string'). Never hard-code API keys or secrets in source files. Configure CI pipelines to inject environment variables during build time, and add fallbacks for missing optional variables to prevent runtime crashes.

Write Jest Tests Alongside Features

Unit tests reside under the tests/ directory and run via Jest. Create test files adjacent to the modules they validate, following the naming convention [feature].test.js or [feature].spec.js as seen in tests/routesRegexp.spec.js.

Mock external dependencies aggressively to ensure tests run quickly and deterministically. The useServices hook architecture makes this straightforward by allowing injection of mock service objects. Always run pnpm test before committing to verify that new functionality does not introduce regressions.

Maintain Code Quality with ESLint

The project uses ESLint configured in eslint.config.mjs for static analysis. Run pnpm lint locally to catch style violations before submitting pull requests. The configuration enforces consistent formatting and catches common React anti-patterns, such as missing hook dependencies or improper prop-types usage.

Summary

  • Inject dependencies through ServicesProvider and consume them via useServices to maintain testable architecture.
  • Configure routes in src/router/Router/routeConfigForPath.js using the declarative pattern rather than manual URL parsing.
  • Isolate core logic within src/core/ and access it through CoreContext to keep UI components decoupled from engine internals.
  • Use pnpm exclusively for package management and follow the Docker-based deployment path for production consistency.
  • Validate environment variables with type checks before use and never commit secrets to the repository.
  • Write Jest tests for new services and utility functions, mocking external APIs to ensure fast, reliable CI passes.

Frequently Asked Questions

How do I add a new API service to stremio-web?

Create the service class in src/services/, export it from src/services/index.js, and register it in the services object passed to ServicesProvider. Access the service in components using the useServices hook rather than importing the class directly. This pattern ensures type safety and simplifies unit testing by allowing mock injection.

What is the correct way to add a new page or route?

Define the route pattern and lazy-loaded component in src/router/Router/routeConfigForPath.js. If the route accepts URL parameters, update src/router/Router/urlParamsForPath.js with the extraction logic. The Router component handles the mapping automatically, so you never need to parse window.location manually inside view components.

How does stremio-web handle translations and localization?

The application initializes the stremio-translations bundle in src/index.js before mounting the React tree. All user-facing strings must reference keys from this bundle rather than hard-coded text. This ensures automatic synchronization when community translators update language packs, and respects the user's selected locale without additional component logic.

Why must core logic stay isolated in the src/core/ directory?

The src/core/ directory contains the low-level Stremio engine integration that manages streaming protocols and metadata parsing. Keeping this logic isolated prevents UI components from becoming coupled to engine implementation details, allowing the core to update independently. Components should interact with core functionality exclusively through CoreContext and CoreProvider abstractions.

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 →