# @rsbuild/plugin-assets-retry

> An Rsbuild plugin to automatically resend requests when static assets fail to load.

Latest version **2.0.2** (published 2026-08-05) · MIT license · 0 weekly downloads

## Install

```sh
npm install @rsbuild/plugin-assets-retry
pnpm add @rsbuild/plugin-assets-retry
yarn add @rsbuild/plugin-assets-retry
bun add @rsbuild/plugin-assets-retry
```

## Health

**Score 75/100 (B)** — status: active.

Positive: has types; esm support; no vulnerabilities; has provenance; recently updated; high maintenance score; high quality score.

Warnings: low downloads.

## Facts

| | |
|---|---|
| Version | 2.0.2 |
| Published | 2026-08-05 |
| First published | 2023-11-09 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | bundled |
| Module format | ESM |
| Dependencies | 0 |
| Unpacked size | 85.7 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| Provenance | attested (GitHub Actions) |
| GitHub stars | 10 |
| Maintainers | chenjiahan, hardfist3 |

## Links

- npm: https://www.npmjs.com/package/@rsbuild/plugin-assets-retry
- Repository: https://github.com/rstackjs/rsbuild-plugin-assets-retry
- Homepage: https://github.com/rstackjs/rsbuild-plugin-assets-retry#readme
- Issues: https://github.com/rstackjs/rsbuild-plugin-assets-retry/issues
- npm.io page: https://npm.io/package/@rsbuild/plugin-assets-retry

## Recent versions

- 2.0.2 (latest) — 2026-08-05
- 1.1.0-beta.0 (beta) — 2025-01-07
- 0.0.0-next-20241121062217 (next) — 2024-11-21
- 1.0.0-alpha.3 (alpha) — 2024-07-04
- 0.0.0-nightly-20240114070311 (nightly) — 2024-01-14
- 2.0.1 — 2026-05-29
- 2.0.0 — 2026-04-27
- 1.5.2 — 2026-02-01
- 1.5.1 — 2026-01-15
- 1.5.0 — 2025-11-05
- 1.4.3 — 2025-09-16
- 1.4.2 — 2025-08-22
- 1.4.1 — 2025-08-03
- 1.4.0 — 2025-07-03
- 1.3.0 — 2025-06-24
- … 190 more at https://npm.io/package/@rsbuild/plugin-assets-retry/versions

## README

# @rsbuild/plugin-assets-retry

An Rsbuild plugin to automatically resend requests when static assets fail to load.

<p>
  <a href="https://npmjs.com/package/@rsbuild/plugin-assets-retry">
   <img src="https://img.shields.io/npm/v/@rsbuild/plugin-assets-retry?style=flat-square&colorA=564341&colorB=EDED91" alt="npm version" />
  </a>
  <img src="https://img.shields.io/badge/License-MIT-blue.svg?style=flat-square&colorA=564341&colorB=EDED91" alt="license" />
  <a href="https://npmcharts.com/compare/@rsbuild/plugin-assets-retry?minimal=true"><img src="https://img.shields.io/npm/dm/@rsbuild/plugin-assets-retry.svg?style=flat-square&colorA=564341&colorB=EDED91" alt="downloads" /></a>
</p>

English | [简体中文](./README.zh-CN.md)

## Quick start

### Install plugin

You can install the plugin using the following command:

```bash
# npm
npm add @rsbuild/plugin-assets-retry -D

# yarn
yarn add @rsbuild/plugin-assets-retry -D

# pnpm
pnpm add @rsbuild/plugin-assets-retry -D

# bun
bun add @rsbuild/plugin-assets-retry -D
```

### Register plugin

You can register the plugin in the `rsbuild.config.ts` file:

```ts
import { pluginAssetsRetry } from '@rsbuild/plugin-assets-retry';

export default {
  plugins: [pluginAssetsRetry()],
};
```

## Options

You can configure the retry behavior for assets retry through the options.

- **Type:**

```ts
type AssetsRetryHookContext = {
  times: number;
  domain: string;
  url: string;
  tagName: string;
  isAsyncChunk: boolean;
};

type RuntimeRetryOptions = {
  type?: string[];
  domain?: string[] | (() => string[]);
  max?: number;
  test?: string | RegExp | ((url: string) => boolean);
  crossOrigin?: boolean | 'anonymous' | 'use-credentials';
  delay?: number | ((context: AssetsRetryHookContext) => number);
  onRetry?: (context: AssetsRetryHookContext) => void;
  onSuccess?: (context: AssetsRetryHookContext) => void;
  onFail?: (context: AssetsRetryHookContext) => void;
};

type AssetsRetryOptions =
  | ({
      inlineScript?: boolean;
      minify?: boolean;
    } & RuntimeRetryOptions)
  | {
      inlineScript?: boolean;
      minify?: boolean;
      rules: RuntimeRetryOptions[];
    };
```

- **Default:**

```ts
const defaultAssetsRetryOptions = {
  max: 3,
  type: ['script', 'link', 'img'],
  domain: [],
  crossOrigin: rsbuildConfig.html.crossorigin,
  delay: 0,
  addQuery: false,
  inlineScript: true,
  minify: rsbuildConfig.mode === 'production',
};
```

### domain

- **Type:** `string[] | (() => string[])`
- **Default:** `[]`

Specifies the retry domain when assets fail to load. In the `domain` array, the first item is the default domain of static assets, and the following items are backup domains. When a asset request for a domain fails, Rsbuild will find that domain in the array and replace it with the next domain in the array.

For example:

```js
// rsbuild.config.ts
defineConfig({
  plugins: [
    pluginAssetsRetry({
      domain: ['cdn1.com', 'cdn2.com', 'cdn3.com'],
    }),
  ],
  output: {
    assetPrefix: 'https://cdn1.com', // or "//cdn1.com"
  },
});
```

After adding the above configuration, when assets fail to load from the `cdn1.com` domain, the request domain will automatically fallback to `cdn2.com`.

If the assets request for `cdn2.com` also fails, the request will fallback to `cdn3.com`.

#### Using string replacement

`domain` generates retry URLs using string replacement, so it supports full URLs starting with `https://` and CDN URLs with a path prefix, such as `https://cdn2.com/foo-path`. When using full URLs, every item in the array must include the protocol. In addition, `output.assetPrefix` cannot use a protocol-relative URL such as `//cdn1.com`, because it cannot be matched by the string replacement.

```js
// rsbuild.config.ts
defineConfig({
  plugins: [
    pluginAssetsRetry({
      domain: [
        'https://cdn1.com',
        'https://cdn2.com/foo-path',
        'https://cdn3.com',
      ],
    }),
  ],
  output: {
    assetPrefix: 'https://cdn1.com',
  },
});
```

#### Resolving the domain at runtime

When the domain list is only known in the browser (for example it is injected on `window` at application startup), pass a function instead of a static array. Like the `onRetry` / `onFail` callbacks, the function is serialized into the runtime script and evaluated in the browser when the retry script starts, so it can read values that do not exist at build time:

```js
// rsbuild.config.ts
defineConfig({
  plugins: [
    pluginAssetsRetry({
      // Evaluated in the browser at startup, not at build time.
      domain: () => [
        // It can be http://localhost:3000 or https://cdn1.com.
        window.location.origin,
        'https://cdn2.com/foo-path',
        'https://cdn3.com',
      ],
    }),
  ],
});
```

It can also work with variables injected into `window`:

```js
// rsbuild.config.ts
defineConfig({
  plugins: [
    pluginAssetsRetry({
      domain: () => {
        const backupDomains =
          window.userRegion === 'cn'
            ? ['https://cdn2.cn/foo-path', 'https://cdn3.cn']
            : ['https://cdn2.com/foo-path', 'https://cdn3.com'];

        return [window.myAssetPath, ...backupDomains];
      },
    }),
  ],
});
```

The returned value is resolved once when the retry script starts and cached for subsequent retries.

> [!NOTE]
> The function must be self-contained: it cannot close over build-time variables (they do not exist in the browser), only over browser globals such as `window`.

### type

- **Type:** `string[]`
- **Default:** `['script', 'link', 'img']`

Used to specify the HTML tag types that need to be retried. By default, script tags, link tags, and img tags are processed, corresponding to JS code, CSS code, and images.

For example, only script tags and link tags are processed:

```js
pluginAssetsRetry({
  type: ['script', 'link'],
});
```

### max

- **Type:** `number`
- **Default:** `3`

The maximum number of retries for a single asset. For example:

```js
pluginAssetsRetry({
  max: 5,
});
```

### test

- **Type:** `string | ((url: string) => boolean) | undefined`
- **Default:** `undefined`

The test function of the asset to be retried. For example:

```js
pluginAssetsRetry({
  test: /cdn\.example\.com/,
});
```

### crossOrigin

- **Type:** `undefined | boolean | 'anonymous' | 'use-credentials'`
- **Default:** `same as html.crossorigin`

When initiating a retry for assets, Rsbuild will recreate the `<script>` tags. This option allows you to set the `crossorigin` attribute for these tags.

By default, the value of `crossOrigin` will be consistent with the `html.crossorigin` configuration, so no additional configuration is required. If you need to configure the recreated tags separately, you can use this option, for example:

```js
pluginAssetsRetry({
  crossOrigin: true,
});
```

### onRetry

- **Type:** `undefined | (context: AssetsRetryHookContext) => void`

The callback function when the asset is being retried. For example:

```js
pluginAssetsRetry({
  onRetry: ({ times, domain, url, tagName, isAsyncChunk }) => {
    console.log(
      `Retry ${times} times, domain: ${domain}, url: ${url}, tagName: ${tagName}, isAsyncChunk: ${isAsyncChunk}`,
    );
  },
});
```

### onSuccess

- **Type:** `undefined | (context: AssetsRetryHookContext) => void`

The callback function when the asset is successfully retried. For example:

```js
pluginAssetsRetry({
  onSuccess: ({ times, domain, url, tagName, isAsyncChunk }) => {
    console.log(
      `Retry ${times} times, domain: ${domain}, url: ${url}, tagName: ${tagName}, isAsyncChunk: ${isAsyncChunk}`,
    );
  },
});
```

### onFail

- **Type:** `undefined | (context: AssetsRetryHookContext) => void`

The callback function when the asset is failed to be retried. For example:

```js
pluginAssetsRetry({
  onFail: ({ times, domain, url, tagName, isAsyncChunk }) => {
    console.log(
      `Retry ${times} times, domain: ${domain}, url: ${url}, tagName: ${tagName}, isAsyncChunk: ${isAsyncChunk}`,
    );
  },
});
```

### addQuery

- **Type:**

```ts
type AddQuery =
  boolean | ((context: { times: number; originalQuery: string }) => string);
```

- **Default:** `false`

Whether to add query when retrying resources, so as to avoid being affected by browser and CDN caches on the retry results.

When set to `true`, `retry=${times}` will be added to the query when requesting, and requests will be made in sequence according to `retry=1`, `retry=2`, `retry=3` etc, for example:

1. Assume that the requested asset is `https://js.cdn.net/foo.js`. If the request fails, it will automatically retry `https://js.cdn.net/foo.js?retry=${times}`

2. Assume that the requested asset is `https://js.cdn.net/foo.js?version=1`. If the request fails, it will automatically retry `https://js.cdn.net/foo.js?version=1&retry=${times}`

When you want to customize query, you can pass a function, for example:

- **Example 1:** All assets requested do not contain query:

```js
pluginAssetsRetry({
  addQuery: ({ times }) => {
    return times === 3
      ? `?retryCount=${times}&isLast=1`
      : `?retryCount=${times}`;
  },
});
```

- **Example 2:** If there is a query in some of the requested assets, you can read it with `originalQuery`:

```js
pluginAssetsRetry({
  addQuery: ({ times, originalQuery }) => {
    const query =
      times === 3 ? `retryCount=${times}&isLast=1` : `retryCount=${times}`;
    return originalQuery ? `${originalQuery}&${query}` : `?${query}`;
  },
});
```

### inlineScript

- **Type:** `boolean`
- **Default:** `true`

Whether to inline the runtime JavaScript code of Assets Retry plugin into the HTML file.

If you don't want to insert the code in the HTML file, you can set `inlineScript` to `false`:

```js
pluginAssetsRetry({
  inlineScript: false,
});
```

After adding the above configuration, the runtime code of Assets Retry plugin will be extracted into a separate `assets-retry.[version].js` file and output to the dist directory.

The downside is that `assets-retry.[version].js` itself may fail to load. If this happens, the assets retry will not work. Therefore, we prefer to inline the runtime code into the HTML file.

### minify

- **Type:** `boolean`
- **Default:** `process.env.NODE_ENV === 'production'`

Configure whether to enable code minification for runtime JavaScript code.

By default, it will be affected by the [output.minify](/config/output/minify) configuration.

```js
pluginAssetsRetry({
  minify: true,
});
```

### delay

- **Type:** `number | ((context: AssetsRetryHookContext) => number)`
- **Default:** `0`

The delay time (in milliseconds) before retrying a failed asset.

You can pass a number:

```js
// Delay 1s before retrying
pluginAssetsRetry({
  delay: 1000,
});
```

Or pass a function that receives `AssetsRetryHookContext` and returns the delay time:

```js
// Calculate delay based on retry attempts
pluginAssetsRetry({
  delay: (ctx) => (ctx.times + 1) * 1000,
});
```

### rules

- **Type:** `RuntimeRetryOptions[]`
- **Default:** `undefined`

Configure multiple retry rules with different options. Each rule will be evaluated in order, and the first matching rule will be used for retry logic. This is useful when you have different retry requirements for different types of assets or domains.

When using `rules`, the plugin will:

1. Check each rule in order by `test` `domain` `type`

2. If the rule is matched, the rule's configuration will be used to retry

3. If no rule is matched, the resource will not be retried

Each rule supports all the same options as the top-level configuration, including `type`, `domain`, `max`, `test`, `crossOrigin`, `delay`, `onRetry`, `onSuccess`, and `onFail`.

- **Example 1:** Different retry strategies for different CDNs:

```js
pluginAssetsRetry({
  rules: [
    {
      // Rule for primary CDN
      test: /cdn1\.example\.com/,
      domain: ['cdn1.example.com', 'cdn1-backup.example.com'],
      max: 3,
      delay: 1000,
    },
    {
      // Rule for secondary CDN with more retries
      test: /cdn2\.example\.com/,
      domain: ['cdn2.example.com', 'cdn2-backup.example.com'],
      max: 5,
      delay: 500,
    },
    {
      // Default rule for other assets
      domain: ['default.example.com', 'default-backup.example.com'],
      max: 2,
    },
  ],
});
```

- **Example 2:** Different retry strategies for different asset types:

```js
pluginAssetsRetry({
  rules: [
    {
      // Critical JavaScript files get more retries
      type: ['script'],
      // Or test: /\.js$/,
      max: 5,
      delay: 1000,
      onFail: ({ url }) => console.error(`Critical JS failed: ${url}`),
    },
    {
      // CSS files get fewer retries
      test: /\.css$/,
      max: 2,
      delay: 500,
    },
    {
      // Images get minimal retries
      test: /\.(png|jpg|gif|svg)$/,
      max: 1,
      delay: 0,
    },
  ],
});
```

## Notes

When you use Assets Retry plugin, the Rsbuild injects some runtime code into the HTML and [Rspack Runtime](https://rspack.rs/misc/glossary#runtime), then serializes the Assets Retry plugin config, inserting it into the runtime code. Therefore, you need to be aware of the following:

- Avoid configuring sensitive information in Assets Retry plugin, such as internal tokens.
- Avoid referencing variables or methods outside of `onRetry`, `onSuccess`, and `onFail`.
- Avoid using syntax with compatibility issues in `onRetry`, `onSuccess` and `onFail` as these functions are inlined directly into the HTML.

Here's an example of incorrect usage:

```js
import { someMethod } from 'utils';

pluginAssetsRetry({
  onRetry() {
    // Incorrect usage, includes sensitive information
    const privateToken = 'a-private-token';

    // Incorrect usage, uses an external method
    someMethod(privateToken);
  },
});
```

## Limitation

Assets Retry plugin may not work in the following scenarios:

### Sync script tag loaded resources

`<script src="..."></script>` tags load resources synchronously, and retrying them does not guarantee the order of resource loading. Therefore, the Assets Retry plugin will not retry resources loaded by synchronous script tags. It will only retry resources loaded by async/defer script tags.

### Module Federation

For remote modules loaded by Module Federation, you can use the [@module-federation/retry-plugin](https://www.npmjs.com/package/@module-federation/retry-plugin) from Module Federation 2.0 to implement static asset retries.

### Micro-frontend

If your project is a micro-frontend application (such as a Garfish sub-application), the assets retry may not work because micro-frontend sub-applications are typically not loaded directly based on the `<script>` tag.

If you need to retry assets in micro-frontend scenarios, please contact the developers of the micro-frontend framework to find a solution.

### Assets in custom templates

Assets Retry plugin listens to the page error event to know whether the current resource fails to load and needs to be retried. Therefore, if the resource in the custom template is executed earlier than Assets Retry plugin, then Assets Retry plugin cannot listen to the event that the resource fails to load, so it will not be retried.

If you want Assets Retry plugin to work on resources in custom templates, you can refer to [Custom Insertion Example](https://github.com/rstackjs/html-rspack-plugin/tree/main/examples/custom-insertion-position) to modify [html.inject](/config/html/inject) configuration and custom template.

```diff
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>custom template</title>
+   <%= htmlPlugin.tags.headTags %>
    <script src="https://example.com/assets/a.js"></script>
  </head>
  <body>
    <div id="root" />
+    <%= htmlPlugin.tags.bodyTags %>
  </body>
</html>
```

#### Identifying retry scripts in HTML templates

The Assets Retry plugin adds a unique `data-rsbuild-assets-retry` attribute to retry scripts, allowing you to easily identify them in custom HTML templates.

You can import the attribute constant:

```js
import { ASSETS_RETRY_DATA_ATTRIBUTE } from '@rsbuild/plugin-assets-retry';
```

The attribute values are:

- `"inline"` for inline scripts (when `inlineScript: true`)
- `"external"` for external scripts (when `inlineScript: false`)

Example usage in HTML templates:

```html
<!-- Filter retry scripts -->
<%= htmlWebpackPlugin.tags.headTags.filter(tag =>
tag.attributes['data-rsbuild-assets-retry'] === 'inline') %>

<!-- Filter non-retry scripts -->
<%= htmlWebpackPlugin.tags.headTags.filter(tag =>
!tag.attributes['data-rsbuild-assets-retry']) %>
```

This allows you to place retry scripts at the top of your HTML head for optimal loading order.

## License

[MIT](./LICENSE).

---
_Source: https://npm.io/package/@rsbuild/plugin-assets-retry · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
