Skip to main content
Stremio Web’s runtime behaviour is managed by five services instantiated once in App.js and distributed to the component tree through React context.

ServicesContext pattern

All services are held in a plain object passed to ServicesProvider (src/services/ServicesContext/ServicesProvider.js):
// App.js
const services = React.useMemo(() => ({
    core:              new Core({ appVersion: process.env.VERSION, shellVersion: null }),
    shell:             new Shell(),
    chromecast:        new Chromecast(),
    keyboardShortcuts: new KeyboardShortcuts(),
    dragAndDrop:       new DragAndDrop({ core })
}), []);

return (
    <ServicesProvider services={services}>
        {children}
    </ServicesProvider>
);
Any component can access services with the useServices hook:
const { core, shell, chromecast } = useServices();
ServicesContext itself is a plain React.createContext({}). There is no Provider abstraction beyond ServicesProvider — the context value is the services object directly.

Service lifecycle

All five services follow the same lifecycle contract:
start() → active = true  → emits 'stateChanged'
stop()  → active = false → emits 'stateChanged'
Services with an async transport (Core, Shell, Chromecast) have three states:
StateMeaning
starting: truestart() called, transport initialising
active: trueTransport ready, service operational
error: ErrorTransport failed, service non-functional
App.js starts all services inside a useEffect and stops them on cleanup:
services.core.start();
services.shell.start();
services.chromecast.start();
services.keyboardShortcuts.start();
services.dragAndDrop.start();

return () => {
    services.core.stop();
    services.shell.stop();
    services.chromecast.stop();
    services.keyboardShortcuts.stop();
    services.dragAndDrop.stop();
};
App.js listens for stateChanged on Core and Shell to set the initialized flag that gates rendering. The app renders a blank screen until both Core and Shell have settled (active or errored).

Core

File: src/services/Core/Core.js Manages the lifecycle of the WASM core and exposes a transport for all communication.

Properties

PropertyTypeDescription
activebooleantrue when the transport is initialised and ready
errorError | nullSet if transport initialisation failed
startingbooleantrue while the transport is initialising
transportCoreTransport | nullThe active transport instance

CoreTransport

File: src/services/Core/CoreTransport.js CoreTransport spawns a Web Worker (worker.js) and wraps the @stremio/stremio-core-web/bridge interface.
const worker = new Worker(`${process.env.COMMIT_HASH}/scripts/worker.js`);
const bridge = new Bridge(window, worker);
Methods:
MethodSignatureDescription
dispatch(action, field)Send an action to a core model. field scopes the action to a specific model instance.
getState(field) → PromiseFetch the current state of a model.
getDebugState() → PromiseFetch the full debug state of the core.
analytics(event)Send an analytics event with the current hash path.
decodeStream(stream) → PromiseDecode a stream descriptor string.
Events emitted on transport:
EventPayloadDescription
NewStatestring[]List of model names whose state changed
CoreEvent{ event, args }Named domain event (e.g. SettingsUpdated)
Events emitted on core:
EventDescription
stateChangedFired when active, error, or starting changes

Window focus sync

When the browser window regains focus and Core is active, App.js dispatches four sync actions:
  • PullAddonsFromAPI
  • PullUserFromAPI
  • SyncLibraryWithAPI
  • PullNotifications
This keeps the app up to date after the user switches away and returns.

Shell

File: src/services/Shell/Shell.js Bridges the native Qt desktop shell when Stremio Web runs inside the Stremio desktop application. In browser-only deployments the transport fails to initialise and shell.error is set, but the app continues running.

ShellTransport

File: src/services/Shell/ShellTransport.js Uses window.qt.webChannelTransport (a Qt WebChannel) to communicate with the host application over a JSON message protocol.
  • On init, sends a QtMsgTypes.init message and waits for the object descriptor.
  • Connects to all signals exposed by the transport Qt object.
  • Exposes a send(event, args) method that invokes the onEvent method on the Qt side.
  • Emits an app-ready signal to the shell once initialised.
Key shell events received from Qt:
EventDescription
open-mediaOpen a stremio:// deep link URL
windowClosedThe native window close button was pressed
App.js handles open-media by parsing the stremio:// URL and redirecting window.location.href to the corresponding hash route.

Chromecast

File: src/services/Chromecast/Chromecast.js Initialises the Google Cast SDK and manages a ChromecastTransport. Errors gracefully when the Cast API is not available (e.g. non-Chrome browsers).

Properties

PropertyTypeDescription
activebooleantrue when the Cast SDK is initialised
errorError | null'Google Cast API not available' if SDK is absent
transportChromecastTransport | nullActive transport for casting control
When Chromecast becomes active, App.js configures it:
services.chromecast.transport.setOptions({
    receiverApplicationId: CONSTANTS.CHROMECAST_RECEIVER_APP_ID,
    autoJoinPolicy: chrome.cast.AutoJoinPolicy.PAGE_SCOPED,
    resumeSavedSession: false,
    androidReceiverCompatible: true
});

KeyboardShortcuts

File: src/services/KeyboardShortcuts/KeyboardShortcuts.js Attaches a global keydown listener to window. Shortcuts are ignored when the user is typing in an INPUT element or when a modifier key (Ctrl, Alt, Shift, Meta) is held.

Shortcuts

KeyAction
0Navigate to #/search
1Navigate to #/ (Board)
2Navigate to #/discover
3Navigate to #/library
4Navigate to #/calendar
5Navigate to #/addons
6Navigate to #/settings
Backspacehistory.back()

Properties

PropertyTypeDescription
activebooleantrue when the keydown listener is attached

DragAndDrop

File: src/services/DragAndDrop/DragAndDrop.js Handles files dropped onto the browser window. Attaches dragover (to prevent the default browser navigation) and drop listeners. Currently handles one file type:
MIME typeAction
application/x-bittorrentReads the file as an ArrayBuffer and dispatches StreamingServer/CreateTorrent to the core
application/x-subripAccepted but not yet handled
text/vttAccepted but not yet handled
Unsupported file types emit an error event with { message, file: { name, type } }. DragAndDrop receives core as a constructor argument because it needs to dispatch actions directly:
const dragAndDrop = new DragAndDrop({ core });

Properties

PropertyTypeDescription
activebooleantrue when the event listeners are attached

Events

EventPayloadDescription
error{ message, file }Emitted when a dropped file cannot be processed
stateChangedEmitted when active changes

Build docs developers (and LLMs) love