close

FAQ

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 to the CDN URL:

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 since the bundles are loaded via ES module imports.

NOTE

This is an improvement over the official webpack5 builder, which does not support CDN asset hosting due to its template always prepending ./ to import paths.

CSS url() references to static assets

In v3.3.2 and earlier, CSS url() worked correctly when served from the root path because the default assetPrefix was '/'. Since v3.3.3, the default changed to '' (relative) for subdirectory support, which introduces this limitation.

When CSS files reference other assets via url() (e.g., background: url('./image.png')), the generated paths may resolve incorrectly in the production build. This happens because Rspack emits url() paths relative to the output root, but the browser resolves them relative to the CSS file's location. This is a known trade-off of using relative paths, and the official Storybook builders have the same limitation.

If your stories rely on CSS url() references, you can switch to absolute paths via output.assetPrefix:

import { mergeRsbuildConfig } from '@rsbuild/core'

const config: StorybookConfig = {
  // --snip--
  rsbuildFinal: (config) => {
    return mergeRsbuildConfig(config, {
      output: {
        assetPrefix: '/',
      },
    })
  },
  // --snip--
}
NOTE

Setting assetPrefix: '/' fixes CSS url() references when served from the root path, but breaks subdirectory deployment. Choose the option that fits your deployment scenario.

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 with the automatic source snippet feature, or manually providing source code through docs source parameters. See the Storybook Docs documentation 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.

// .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
                },
              }),
            ],
          })
        },
      },
    })
  },
}

Why do sandboxes use getAbsolutePath to resolve the framework?

See Storybook's FAQ for the explanation.

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

Rsbuild offers a CLI debug mode. Enable it when running Storybook to dump the generated configuration paths.

In development:

DEBUG=rsbuild storybook dev

In production:

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 to learn how.