Skip to main content
Chrome extensions require specific build configurations, especially for content scripts which run in an isolated context and cannot use ES modules or dynamic imports.
Rsbuild is a high-performance build tool based on Rspack. For Chrome extensions, you must disable code splitting to avoid ChunkLoadError issues:
// rsbuild.config.ts
import { defineConfig } from '@rsbuild/core';

export default defineConfig({
  source: {
    entry: {
      background: './src/background/index.ts',
      content: './src/content/index.ts',
    },
  },
  output: {
    target: 'web',
    distPath: {
      root: 'dist',
      js: '',
    },
    filename: {
      js: '[name].js',
    },
    copy: [
      { from: './manifest.json', to: './' },
    ],
    charset: 'ascii',
  },
  html: {
    inject: false,
  },
  tools: {
    htmlPlugin: false,
    rspack: {
      output: {
        chunkLoading: false,
        asyncChunks: false,
      },
      optimization: {
        splitChunks: false,
        runtimeChunk: false,
      },
    },
  },
  performance: {
    chunkSplit: { strategy: 'all-in-one' },
  },
  dev: {
    hmr: false,
    liveReload: false,
  },
});
Build the extension:
# Development build
npm run build

# Production build
NODE_ENV=production rsbuild build
The key configuration for Chrome extensions is disabling all code splitting (asyncChunks: false, splitChunks: false, strategy: 'all-in-one'). This ensures all code is bundled into single files, avoiding dynamic import issues in content scripts.

Dynamic Loading (Optional)

Load the SDK only when needed:
// Lazy load for popup
document.addEventListener('DOMContentLoaded', async () => {
  if (userHasOptedIn()) {
    const { default: composite } = await import('@composite-inc/composite-js');
    await composite.init({
      apiKey: 'pk_your_api_key',
      transport: 'chrome-extension'
    });
  }
});

What’s Next?