How Data Is Fetched and Managed in Stremio-Web Components: Web Worker Bridge Explained
Stremio-web delegates all data operations to a Web Worker via a CoreTransport bridge, allowing React components to fetch immutable state snapshots and dispatch Redux-like actions without blocking the UI thread.
Stremio-web is the official web frontend for the Stremio media center platform. Rather than managing state through traditional HTTP requests or client-side stores, stremio-web uses a Web Worker Bridge to communicate with @stremio/stremio-core-web. This architecture separates UI rendering from data management, ensuring the interface remains responsive while the core handles torrent parsing, API calls, and state mutations.
Core Transport Layer: The Bridge to Stremio Core
The data fetching pipeline originates in src/core/createTransport.ts, which constructs a CoreTransport object wrapping a Bridge instance. This transport forwards all communication to the Web Worker running the Stremio core logic.
The transport exposes five async methods via RPC-style calls:
init(args)— initializes the core with configuration objectsgetState(model)— returns the current snapshot of a specific core model (e.g.,profile,library,streams)dispatch(action, model?)— sends Redux-like actions to mutate core stateencodeStream / decodeStream— helpers for stream URL encodinganalytics(event)— transmits telemetry data
// src/core/createTransport.ts
const createTransport = (): CoreTransport => {
const init = (args: object) => bridge.call(['init'], [args]);
const getState = (model: string) => bridge.call(['getState'], [model]);
const dispatch = (action: DispatchAction, model?: string) =>
bridge.call(['dispatch'], [action, model, location.hash]);
// ... encodeStream, decodeStream, analytics implementations
return { init, getState, dispatch, encodeStream, decodeStream, analytics };
};
Every method travels through bridge.call, which serializes requests and posts them to the Web Worker, keeping the main thread free for rendering.
React Context Integration
To make the transport available throughout the component tree, stremio-web implements a React Context pattern defined in src/core/CoreContext.ts and provided via src/core/CoreProvider.tsx.
The provider instantiates the transport exactly once using React.useMemo, ensuring a single Web Worker connection persists across the application lifecycle:
// src/core/CoreProvider.tsx
const CoreProvider: React.FC = ({ children }) => {
const transport = React.useMemo(createTransport, []);
return (
<CoreContext.Provider value={transport}>
{children}
</CoreContext.Provider>
);
};
Components access this transport through the useCore hook (src/core/useCore.ts), which consumes CoreContext and returns the transport instance. This pattern eliminates prop drilling and provides universal access to data fetching and action dispatching.
Data-Fetching Hooks for UI Components
Stremio-web abstracts direct transport calls into reusable React hooks located in src/common/. These hooks manage subscription logic, re-rendering when models update, and proper cleanup on unmount.
useModelState: The Generic Model Fetcher
The useModelState hook (src/common/useModelState.js) is the primary mechanism for how data is fetched and managed in stremio-web components. It accepts a model name (e.g., 'library', 'recommendations', 'settings'), calls transport.getState(model) on mount, and subscribes to subsequent updates broadcast from the Web Worker.
// src/common/useModelState.js usage
import useModelState from '../common/useModelState';
const Library = () => {
const library = useModelState('library'); // Calls transport.getState('library')
if (!library) return null;
return (
<ul>
{library.items.map(item => (
<li key={item.id}>{item.title}</li>
))}
</ul>
);
};
Specialized Hooks for Domain Models
For frequently accessed data, stremio-web provides specialized wrappers that enhance the base useModelState functionality:
useProfile(src/common/useProfile.js) — fetches theprofilemodel and exposes convenience methods likelogin()andlogout()that wrap dispatch callsuseSettings— retrieves user preferences from thesettingsmodeluseStreamingServer(src/common/useStreamingServer.js) — manages streaming server configuration and health checksusePlayUrl(src/common/usePlayUrl.ts) — resolves playable URLs for specific streams by communicating with the core's stream resolution logic
These hooks follow a consistent pattern: they call getState for their respective models and return the immutable snapshot data to the consuming component.
Dispatching Actions to Modify State
While fetching data handles read operations, user interactions require writing state back to the core. When users add items to their library, play streams, or modify settings, components dispatch actions through the transport.
Components obtain the dispatch function either directly from useCore() or from specialized hooks that wrap the transport:
// Direct dispatch from component
import { useCore } from '../core/useCore';
function AddToLibraryButton({ item }) {
const core = useCore(); // Accesses transport from CoreContext
const handleClick = () => {
core.dispatch({ type: 'ADD_TO_LIBRARY', payload: item });
};
return <button onClick={handleClick}>Add to library</button>;
}
The dispatch method forwards the action to bridge.call(['dispatch'], [action, model, location.hash]), which the Web Worker processes asynchronously. The core updates its internal state and broadcasts new snapshots to subscribed models, automatically triggering React re-renders in components using useModelState or similar hooks.
Web Worker Lifecycle and Performance Characteristics
The Web Worker is instantiated exactly once via new Worker(${process.env.COMMIT_HASH}/scripts/worker.js) when createTransport first executes. All RPC traffic between the UI thread and the core runs through this single bridge instance.
This architecture provides specific performance benefits:
- Non-blocking UI thread: Heavy operations like torrent parsing, metadata fetching, and cryptographic operations execute in the worker thread
- Immutable state snapshots: The core maintains a single source of truth; components receive fresh snapshots via
getStaterather than mutable references - Selective subscriptions: Components only re-render when their specific requested models update, not on every global state change
Summary
- Stremio-web uses a Web Worker Bridge (
src/core/createTransport.ts) to isolate core logic from the React UI thread - The CoreTransport exposes
getState(model)for reading data anddispatch(action)for mutations via RPC - React Context (
src/core/CoreProvider.tsx) injects the transport into the component tree, accessed viauseCore() - Custom hooks like
useModelState(src/common/useModelState.js) anduseProfile(src/common/useProfile.js) wrap transport calls and manage model subscriptions - All data fetching and state management occurs asynchronously through the Web Worker, ensuring the main thread remains responsive during intensive operations
Frequently Asked Questions
How does stremio-web fetch data without freezing the user interface?
Stremio-web instantiates a Web Worker that runs the Stremio core logic in a background thread. The createTransport.ts file establishes a bridge that forwards all getState and dispatch calls to this worker via bridge.call. Because the core executes off the main thread, expensive operations like parsing torrent streams or fetching metadata never block React's render cycle.
What is the difference between useCore and useModelState in stremio-web?
useCore() (src/core/useCore.ts) returns the raw CoreTransport object containing methods like getState, dispatch, and analytics. useModelState (src/common/useModelState.js) is a higher-level hook that internally calls useCore, invokes transport.getState(model) for a specific model name, and manages the subscription lifecycle including cleanup. Most components should use useModelState or specialized hooks like useProfile rather than accessing the transport directly.
Can components dispatch actions to multiple models simultaneously?
Yes. The dispatch method accepts an optional second parameter specifying the target model. When a component calls core.dispatch({ type: 'PLAY', payload: stream }), the transport forwards this action to the Web Worker, which routes it to the appropriate model handler. The core processes the update, modifies its internal state, and broadcasts changes back to any subscribed components listening to affected models.
Where is the Web Worker initialized in the stremio-web source code?
The Web Worker is created inside the Bridge implementation when createTransport is first invoked, which occurs in src/core/CoreProvider.tsx during the initial render. The worker script loads from ${process.env.COMMIT_HASH}/scripts/worker.js. This single instance persists for the entire application lifetime, handling all RPC communication between the React UI and the Stremio core according to the implementation in src/core/createTransport.ts.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →