> For AI agents: the complete documentation index is available at /llms.txt, the full documentation bundle is available at /llms-full.txt.

# Troubleshooting

## Deploy Storybook to a subdirectory or subpath

> Since v3.3.3. In v3.3.2 and earlier, the default `assetPrefix` was `'/'` (absolute), which required manual configuration for subdirectory deployment.

The Rsbuild builder uses relative asset paths by default — the same as the official Vite (`base: './'`) and webpack (`publicPath: ''`) builders. Storybook can be deployed to any subdirectory (e.g., `https://example.com/storybook/`) without extra configuration.

## Serving static assets from a CDN

> Since v3.3.3. In v3.3.2 and earlier, this was not supported.

To serve Storybook's JS/CSS bundles from a separate CDN origin (e.g., `https://cdn.example.com/assets/`), set [`output.assetPrefix`](https://rsbuild.rs/config/output/asset-prefix) to the CDN URL:

```js
import { mergeRsbuildConfig } from '@rsbuild/core'

const config: StorybookConfig = {
  // --snip--
  rsbuildFinal: (config) => {
    return mergeRsbuildConfig(config, {
      output: {
        assetPrefix: 'https://cdn.example.com/',
      },
    })
  },
  // --snip--
}
```

The preview template correctly handles absolute URLs, so JS/CSS bundles will be loaded from the CDN while `iframe.html` can remain on the origin server. Note that the CDN must serve appropriate [CORS headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) since the bundles are loaded via ES module imports.

> \[!NOTE]
> This is an improvement over the [official webpack5 builder](https://github.com/storybookjs/storybook/issues/23481), which does not support CDN asset hosting due to its template always prepending `./` to import paths.

## `Template.toString()` shows compiled code in Docs source snippet

When using the CSF 2 `Template.bind({})` pattern and `Template.toString()` to display source code in the Docs addon, the snippet shows bundler-compiled output instead of your original source.

This is expected — `Function.prototype.toString()` serializes the **runtime** function, which has already been transformed by the bundler. This is not specific to the Rsbuild builder; the same happens with webpack and Vite.

Storybook recommends using the [CSF 3 format](https://storybook.js.org/docs/api/csf) with the automatic source snippet feature, or manually providing source code through [docs source parameters](https://storybook.js.org/docs/api/doc-blocks/doc-block-source). See the [Storybook Docs documentation](https://storybook.js.org/docs/writing-docs/autodocs) for more details.

## Build errors from unexpected files

> \[!NOTE]
> This issue mostly affects older Rspack versions. Modern Rspack handles `webpackInclude` correctly.

If you encounter issues where Rspack bundles files it shouldn't (like Markdown files in unexpected places), you can use `rspack.IgnorePlugin` to exclude them.

```js
// .storybook/main.js
import path from 'path'
import { mergeRsbuildConfig } from '@rsbuild/core'

export default {
  framework: 'storybook-react-rsbuild',
  async rsbuildFinal(config) {
    return mergeRsbuildConfig(config, {
      tools: {
        rspack: (config, { addRules, appendPlugins, rspack, mergeConfig }) => {
          return mergeConfig(config, {
            plugins: [
              new rspack.IgnorePlugin({
                checkResource: (resource, context) => {
                  const absPathHasExt = path.extname(resource)
                  if (absPathHasExt === '.md') {
                    return true
                  }

                  return false
                },
              }),
            ],
          })
        },
      },
    })
  },
}
```

## MSW integration and lazy compilation

When `mockServiceWorker.js` is found in one of the `staticDirs` entries — the convention used by [`msw-storybook-addon`](https://github.com/mswjs/msw-storybook-addon) and any hand-rolled MSW setup — the builder automatically disables lazy compilation and logs a warning at startup.

This is necessary because MSW's Service Worker intercepts every `fetch` from the preview iframe, including the dev-server's lazy-compilation RPC (`POST /lazy-compilation-using-`). The SW's passthrough re-issues a fresh `fetch(clonedRequest)` that the dev-server aborts, which can leave stories stuck on a blank iframe during cold loads. The issue is rooted in how the lazy-compilation runtime talks to the dev-server via a stateful RPC channel — not a defect of the builder itself — and also affects the official `@storybook/builder-webpack5` when lazy compilation is manually enabled. A related failure mode is discussed upstream in [mswjs/msw#834](https://github.com/mswjs/msw/issues/834) (SW intercepting dev-server HMR SSE).

Leaving the auto-disable in place is the safest choice. If you never explicitly set `lazyCompilation`, no action is required on your side.

If your project needs lazy compilation alongside MSW, set `lazyCompilation` explicitly in your builder options — an explicit value always takes priority over the MSW auto-disable:

```ts
// .storybook/main.ts
export default {
  framework: {
    options: {
      builder: {
        // Opt back in — expect occasional blank iframes on cold loads.
        lazyCompilation: { entries: false },
      },
    },
  },
}
```

When you opt back in, patch `public/mockServiceWorker.js` to let lazy-compilation RPC bypass MSW. Add this guard at the top of the `fetch` event listener, before MSW's own logic runs:

```js
// public/mockServiceWorker.js
self.addEventListener('fetch', function (event) {
  // Let Rspack / webpack's lazy-compilation RPC bypass MSW so the
  // dev-server sees the original client request, not a re-issued clone.
  if (event.request.url.includes('/lazy-compilation-using-')) {
    return
  }
  // ...existing MSW handler logic
})
```

Caveats:

- `msw init` regenerates `mockServiceWorker.js` and will overwrite the patch. Re-apply it after upgrading MSW, or automate via a postinstall script that appends the guard if it is missing.
- The bypass is scoped strictly to the lazy-compilation path; normal story requests still go through MSW and your handlers behave as before.
- The substring match covers both the Rspack implementation (same-origin `POST /lazy-compilation-using-`) and webpack5's (dedicated random-port `GET /lazy-compilation-using-<module-path>`), so the same guard also works if you ever move to the official webpack5 builder with lazy compilation enabled.

## Why do sandboxes use `getAbsolutePath` to resolve the framework?

See [Storybook's FAQ](https://storybook.js.org/docs/faq#how-do-i-fix-module-resolution-in-special-environments) for the explanation.

## How can I inspect the Rsbuild or Rspack config used by Storybook?

Rsbuild offers a [CLI debug mode](https://rsbuild.rs/guide/debug/debug-mode). Enable it when running Storybook to dump the generated configuration paths.

In development:

```bash
DEBUG=rsbuild storybook dev
```

In production:

```bash
DEBUG=rsbuild storybook build
```

Check the CLI output to see where the Rsbuild and Rspack configs are written.

## Can I use this builder with Rspack directly?

Yes. Rsbuild is built on Rspack, so you can feed your Rspack configuration into the Rsbuild builder. Follow the [Rspack integration guide](/guide/integrations/rspack.md) to learn how.
