# How State Management Solutions Are Implemented in Stremio-Web

> Discover how stremio-web implements its state management using React Contexts, a custom useModelState hook, and domain-specific wrappers for efficient, declarative state access without flux libraries.

- Repository: [Stremio/stremio-web](https://github.com/Stremio/stremio-web)
- Tags: internals
- Published: 2026-05-23

---

**Stremio-web implements state management through a lightweight React-centric architecture that combines React Contexts for global dependency injection, a custom `useModelState` hook for Core synchronization, and domain-specific wrapper hooks that provide declarative access to application state without external flux libraries.**

Stremio-web relies on a custom state management architecture that leverages React primitives to synchronize UI components with the underlying Stremio Core engine. Unlike traditional React applications that might use Redux or MobX, the state management solutions implemented in stremio-web center on the `useModelState` hook and a minimal Context API pattern. This approach eliminates unnecessary overhead while maintaining performant, reactive updates across the component tree.

## React Contexts for Global Services

Stremio-web uses React Contexts to provide immutable global objects—such as the Core services instance, routing focus flags, and modal-container references—to any component in the tree. These contexts are created with `React.createContext` and supplied by matching provider components.

The primary global context is defined in [`src/services/ServicesContext/ServicesContext.js`](https://github.com/Stremio/stremio-web/blob/main/src/services/ServicesContext/ServicesContext.js), which exports a context for the services map. The concrete implementation is injected via [`src/services/ServicesContext/ServicesProvider.js`](https://github.com/Stremio/stremio-web/blob/main/src/services/ServicesContext/ServicesProvider.js), which wraps the application and supplies the Core, Chromecast, Gamepad, and other services. Additional contexts include `RouteFocusedContext` ([`src/router/RouteFocusedContext/RouteFocusedContext.js`](https://github.com/Stremio/stremio-web/blob/main/src/router/RouteFocusedContext/RouteFocusedContext.js)) for tracking route focus and `ModalsContainerContext` ([`src/router/ModalsContainerContext/ModalsContainerContext.js`](https://github.com/Stremio/stremio-web/blob/main/src/router/ModalsContainerContext/ModalsContainerContext.js)) for sharing modal handling across the app.

Components access these services through the `useServices` hook:

```javascript
import { useServices } from 'stremio/services';

const Player = () => {
    const services = useServices();
    // services.core exposes the Stremio Core instance for low-level operations
    return <div>{/* Player implementation */}</div>;
};

```

## The useModelState Hook Pattern

At the core of Stremio-web's state management is the `useModelState` hook, located in [`src/common/useModelState.js`](https://github.com/Stremio/stremio-web/blob/main/src/common/useModelState.js). This hook creates a reactive bridge between React components and the Stremio Core's model-based state architecture.

### How useModelState Connects to the Core

The hook obtains the Stremio Core instance via `useCore()` and registers a throttled listener on the Core's `state` event. When the Core emits state updates, the hook filters them based on the requested model name (e.g., `library`, `player`, `search`) and optional dependencies. It retrieves the latest model state using `core.transport.getState` and optionally transforms the raw state through a user-supplied `map` callback. The processed state is stored in a `useReducer`-based container to ensure proper batching and deduplication.

The hook also manages lifecycle actions: it dispatches an initial `action` if supplied (typically to load data) and ensures an `Unload` action fires on unmount to free resources:

```javascript
// src/common/useModelState.js (conceptual structure)
const useModelState = ({ model, action, map }) => {
    const core = useCore();
    const [state, dispatch] = useReducer(reducer, initialState);
    
    useEffect(() => {
        const listener = throttle(() => {
            const coreState = core.transport.getState();
            const modelSlice = coreState[model];
            dispatch({ type: 'UPDATE', payload: map ? map(modelSlice) : modelSlice });
        }, 100);
        
        core.on('state', listener);
        if (action) core.dispatch({ ...action, model });
        
        return () => {
            core.off('state', listener);
            core.dispatch({ action: 'Unload', model });
        };
    }, [model, action]);
    
    return state;
};

```

### Core Suspension for Safe Initialization

Before any model access occurs, Stremio-web guarantees Core readiness through the `CoreSuspender` pattern ([`src/common/CoreSuspender.js`](https://github.com/Stremio/stremio-web/blob/main/src/common/CoreSuspender.js)). The `useCoreSuspender()` hook returns `{ getState, setState }` methods that `useModelState` uses to safely read the initial snapshot. This prevents race conditions where components might attempt to access Core state before the transport layer is fully initialized.

## Domain-Specific State Hooks

Rather than exposing `useModelState` directly throughout the UI, Stremio-web provides convenient wrapper hooks for specific features. Each follows the same declarative pattern: `useModelState({ model: 'model_name', action, map })`.

**useLibrary** ([`src/routes/Library/useLibrary.js`](https://github.com/Stremio/stremio-web/blob/main/src/routes/Library/useLibrary.js)) manages the user's content library:

```javascript
import { useModelState } from 'stremio/common';

export const useLibrary = (action) =>
    useModelState({
        model: 'library',
        action,
        map: (state) => ({
            items: state.items ?? [],
            loading: state.loading ?? false,
        })
    });

```

**usePlayer** ([`src/routes/Player/usePlayer.js`](https://github.com/Stremio/stremio-web/blob/main/src/routes/Player/usePlayer.js)) handles playback state and stream selection.

**useSearch** ([`src/routes/Search/useSearch.js`](https://github.com/Stremio/stremio-web/blob/main/src/routes/Search/useSearch.js)) manages search query state and results.

**useEvents** ([`src/components/EventModal/useEvents.js`](https://github.com/Stremio/stremio-web/blob/main/src/components/EventModal/useEvents.js)) connects to the `ctx` model for application-wide events.

These hooks demonstrate how `useModelState` enables component-level state shaping while maintaining a consistent interface to the Core.

## Application Bootstrap Pattern

The top-level [`src/App/App.js`](https://github.com/Stremio/stremio-web/blob/main/src/App/App.js) orchestrates the state management architecture by wrapping the entire UI with `ServicesProvider`. This injection makes the Core and other services available to all descendants through React Context:

```javascript
// src/App/App.js (simplified)
import { ServicesProvider } from 'stremio/services';
import { Router } from 'stremio/router';

const App = () => (
    <ServicesProvider>
        <Router>
            {/* Route components call useLibrary, usePlayer, etc. */}
        </Router>
    </ServicesProvider>
);

```

Route components then import their respective domain hooks (e.g., `useLibrary` for the Library route), which internally call `useModelState`. This keeps the UI reactive to any Core state change while avoiding prop drilling or heavyweight external state libraries.

## Summary

- **React Contexts** (`ServicesContext`, `RouteFocusedContext`, etc.) provide global service injection without prop drilling.
- **`useModelState`** ([`src/common/useModelState.js`](https://github.com/Stremio/stremio-web/blob/main/src/common/useModelState.js)) is the primary hook connecting components to Stremio Core models via throttled event listeners and `useReducer`.
- **CoreSuspender** ([`src/common/CoreSuspender.js`](https://github.com/Stremio/stremio-web/blob/main/src/common/CoreSuspender.js)) ensures safe initialization before any state reads occur.
- **Domain hooks** (e.g., `useLibrary`, `usePlayer`) wrap `useModelState` to provide type-safe, feature-specific state accessors.
- The architecture avoids Redux or MobX, relying instead on React primitives and the Stremio Core's event-driven state machine.

## Frequently Asked Questions

### Does Stremio-web use Redux or MobX for state management?

No. According to the Stremio-web source code, the application avoids external flux libraries entirely. Instead, it uses a custom `useModelState` hook combined with React Contexts to synchronize components with the Stremio Core engine, resulting in a smaller bundle size and simpler mental model.

### What is the useModelState hook used for?

The `useModelState` hook ([`src/common/useModelState.js`](https://github.com/Stremio/stremio-web/blob/main/src/common/useModelState.js)) connects React components to specific models in the Stremio Core (such as `library`, `player`, or `search`). It handles subscription management, throttled updates, automatic loading/unloading of model data, and optional state transformation through a `map` callback.

### How does Stremio-web handle Core initialization before state access?

Stremio-web implements a **CoreSuspender** pattern ([`src/common/CoreSuspender.js`](https://github.com/Stremio/stremio-web/blob/main/src/common/CoreSuspender.js)) that guarantees the Core transport layer is ready before any state reads occur. The `useCoreSuspender()` hook provides `getState` and `setState` methods that `useModelState` uses to safely retrieve the initial state snapshot, preventing race conditions during application startup.

### Can I create custom state hooks for new features in Stremio-web?

Yes. New features should follow the established pattern of creating a wrapper hook around `useModelState`. Declare the hook in your feature directory (e.g., [`src/routes/MyFeature/useMyFeature.js`](https://github.com/Stremio/stremio-web/blob/main/src/routes/MyFeature/useMyFeature.js)), call `useModelState` with the appropriate model name and optional `action` and `map` parameters, and export the hook for use in your components. This maintains consistency with the existing `useLibrary` and `usePlayer` implementations.