# How to Integrate Custom Addons with Stremio Web: A Complete Technical Guide

> Integrate custom addons with Stremio Web using URL parameters and the useRemoteAddons hook. This technical guide shows remote catalog fetching without local installation. Learn more now.

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

---

**Stremio Web integrates custom addons by parsing URL parameters (`type`, `catalogId`, `transportUrl`) in the `useRemoteAddons` hook and dispatching a Load action to the `CatalogWithFilters` model, enabling remote catalog fetching without local installation.**

The Stremio Web client provides a flexible architecture for loading third-party content sources through its Addons route. According to the Stremio/stremio-web source code, the application distinguishes between locally installed addons and remote catalogs, allowing developers to integrate custom addons either through deep links or direct programmatic access.

## Understanding the Addon Integration Architecture

Stremio Web manages addon discovery through two specialized React hooks that handle different data sources.

**`useRemoteAddons`** (located in [`src/routes/Addons/useRemoteAddons.js`](https://github.com/Stremio/stremio-web/blob/main/src/routes/Addons/useRemoteAddons.js)) constructs the Load/Unload actions for remote catalogs based on URL query parameters. When valid parameters are detected, this hook generates the necessary model state to fetch content from an external manifest.

**`useInstalledAddons`** (located in [`src/routes/Addons/useInstalledAddons.js`](https://github.com/Stremio/stremio-web/blob/main/src/routes/Addons/useInstalledAddons.js)) serves as a fallback mechanism, loading the list of locally persisted addons when no remote parameters are present in the URL.

The main **Addons** component ([`src/routes/Addons/Addons.js`](https://github.com/Stremio/stremio-web/blob/main/src/routes/Addons/Addons.js)) orchestrates these hooks, rendering the catalog selector UI and handling the installation flow through `useAddonDetailsTransportUrl`.

## Preparing Your Custom Addon

Before integration, your addon must expose a JSON manifest compliant with the Stremio addon specification. The manifest URL acts as the `transportUrl` parameter.

Ensure your manifest includes:
- Valid content types (`movie`, `series`, `channel`, etc.)
- Catalog definitions with unique identifiers
- Proper CORS headers for cross-origin requests

## Method 1: URL-Based Integration (Deep Linking)

The simplest way to integrate a custom addon is through URL parameters that trigger the remote loading mechanism.

Construct a URL with three required parameters:
- **`type`** — The primary content type (e.g., `movie`, `series`)
- **`catalogId`** — The catalog identifier defined in your manifest (e.g., `mycatalog`)
- **`transportUrl`** — URL-encoded address of the manifest file

```text
https://app.strem.io/addons?type=movie&catalogId=mycatalog&transportUrl=https%3A%2F%2Fmyserver.com%2Fmanifest.json

```

When a user navigates to this URL, `useRemoteAddons` validates the parameters and returns a **Load** action:

```javascript
{
    action: 'Load',
    args: {
        model: 'CatalogWithFilters',
        args: {
            request: {
                base: urlParams.transportUrl,
                path: {
                    resource: 'addon_catalog',
                    type: urlParams.type,
                    id: urlParams.catalogId,
                    extra: []
                }
            }
        }
    }
}

```

This action is passed to `useModelState` (from `stremio/common`), which instructs the core to fetch the remote catalog via [`src/core/createTransport.ts`](https://github.com/Stremio/stremio-web/blob/main/src/core/createTransport.ts).

## Method 2: Programmatic Integration

For components that need to load custom addons without modifying the URL, reuse the same model action pattern directly.

Create a custom hook that wraps `useModelState`:

```javascript
const React = require('react');
const { useModelState } = require('stremio/common');

const useCustomAddon = (manifestUrl, type, catalogId) => {
    const action = React.useMemo(() => ({
        action: 'Load',
        args: {
            model: 'CatalogWithFilters',
            args: {
                request: {
                    base: manifestUrl,
                    path: {
                        resource: 'addon_catalog',
                        type,
                        id: catalogId,
                        extra: []
                    }
                }
            }
        }
    }), [manifestUrl, type, catalogId]);

    return useModelState({ 
        model: 'remote_addons', 
        action, 
        deps: ['ctx'] 
    });
};

// Usage in a component
function MyComponent() {
    const manifestUrl = 'https://myserver.com/manifest.json';
    const { selectable, selected } = useCustomAddon(manifestUrl, 'movie', 'mycatalog');
    
    // selectable contains available catalogs
    // selected contains the active catalog data
    return null;
}

```

## Core Implementation Details

### The Remote Addons Hook ([`useRemoteAddons.js`](https://github.com/Stremio/stremio-web/blob/main/useRemoteAddons.js))

Located at [`src/routes/Addons/useRemoteAddons.js`](https://github.com/Stremio/stremio-web/blob/main/src/routes/Addons/useRemoteAddons.js), this hook reads URL search parameters using `useSearchParams`. When all three parameters (`type`, `catalogId`, `transportUrl`) are present and valid strings, it constructs the Load action for the `CatalogWithFilters` model. If parameters are missing or invalid, it returns an **Unload** action to clear the remote state.

### The Installed Addons Fallback ([`useInstalledAddons.js`](https://github.com/Stremio/stremio-web/blob/main/useInstalledAddons.js))

Found at [`src/routes/Addons/useInstalledAddons.js`](https://github.com/Stremio/stremio-web/blob/main/src/routes/Addons/useInstalledAddons.js), this hook loads the user's locally installed addons as a fallback. It queries the core model for installed addon data, providing the base list shown in the Addons selector when no remote URL parameters are active.

### The Addons Component ([`Addons.js`](https://github.com/Stremio/stremio-web/blob/main/Addons.js))

The main container at [`src/routes/Addons/Addons.js`](https://github.com/Stremio/stremio-web/blob/main/src/routes/Addons/Addons.js) composes both hooks and manages the UI state. It uses `useSelectableInputs` (from [`src/routes/Addons/useSelectableInputs.js`](https://github.com/Stremio/stremio-web/blob/main/src/routes/Addons/useSelectableInputs.js)) to merge remote and installed catalogs into a unified selector. When users interact with the "Add Addon" input, it updates `addonDetailsTransportUrl` via `useAddonDetailsTransportUrl`, syncing the modal input with the route parameters.

### Transport Layer ([`createTransport.ts`](https://github.com/Stremio/stremio-web/blob/main/createTransport.ts))

The core transport implementation in [`src/core/createTransport.ts`](https://github.com/Stremio/stremio-web/blob/main/src/core/createTransport.ts) handles the actual HTTP requests to fetch remote manifests. When the `CatalogWithFilters` model receives a Load action, it uses this transport layer to retrieve the addon manifest and subsequent catalog data, parsing the responses into the Stremio content format.

## Summary

- **Stremio Web** distinguishes between remote and installed addons through separate hooks in the Addons route.
- **URL parameters** (`type`, `catalogId`, `transportUrl`) trigger automatic loading via `useRemoteAddons` when navigating to `/addons`.
- **Programmatic access** requires creating a Load action for the `CatalogWithFilters` model and passing it to `useModelState` with the `remote_addons` model key.
- **Core files** involved include [`useRemoteAddons.js`](https://github.com/Stremio/stremio-web/blob/main/useRemoteAddons.js), [`useInstalledAddons.js`](https://github.com/Stremio/stremio-web/blob/main/useInstalledAddons.js), [`Addons.js`](https://github.com/Stremio/stremio-web/blob/main/Addons.js), and [`createTransport.ts`](https://github.com/Stremio/stremio-web/blob/main/createTransport.ts).
- **Manifest compliance** is required—custom addons must serve valid JSON manifests with proper CORS configuration.

## Frequently Asked Questions

### How does Stremio Web validate custom addon URLs?

Stremio Web validates custom addon URLs in [`src/routes/Addons/useRemoteAddons.js`](https://github.com/Stremio/stremio-web/blob/main/src/routes/Addons/useRemoteAddons.js) by checking that `type`, `catalogId`, and `transportUrl` are all non-empty strings. If any parameter is missing or invalid, the hook returns an Unload action instead of attempting to fetch the remote catalog.

### Can I integrate a custom addon without installing it permanently?

Yes. When you access an addon via URL parameters or programmatic Load actions, Stremio Web treats it as a remote catalog without persisting it to local storage. The addon remains available only for the current session unless the user explicitly clicks the Install button rendered by the `Addon` component in [`src/routes/Addons/Addon/Addon.js`](https://github.com/Stremio/stremio-web/blob/main/src/routes/Addons/Addon/Addon.js).

### What model does Stremio Web use to fetch addon catalogs?

Stremio Web uses the **CatalogWithFilters** model to fetch addon catalogs. As implemented in the source code, this model accepts a request object with a `base` (the manifest URL) and a `path` specifying the resource type, content type, catalog ID, and extra parameters. The core then uses [`createTransport.ts`](https://github.com/Stremio/stremio-web/blob/main/createTransport.ts) to execute the HTTP request.

### How do I test a custom addon during development?

You can test a custom addon by navigating to the Addons page with your manifest URL as the `transportUrl` parameter, or by using the Discover flow in [`src/routes/Discover/Discover.js`](https://github.com/Stremio/stremio-web/blob/main/src/routes/Discover/Discover.js). For programmatic testing, create a temporary component that calls `useModelState` with a manually constructed Load action pointing to your local or staging manifest endpoint.