Advanced

May 20, 2026 ยท View on GitHub

Jetpack's defaults are meant to cover the common path. When they do not, use the rspack hook.

Rspack

import rspack from 'jetpack/rspack'

export default {
  rspack: (config, context) => {
    config.module.rules[0].oneOf.unshift({
      test: /\.svg$/,
      use: ['@svgr/webpack']
    })

    config.plugins.push(
      new rspack.DefinePlugin({
        __EXPERIMENT__: JSON.stringify(true)
      })
    )

    return config
  }
}

The second argument is { command, mode, target, dir, findLoader }, where target is the generated bundle target: 'modern' or 'legacy'.

Use findLoader(name) to tweak Jetpack's generated loader options without depending on the internal rule nesting:

export default {
  rspack: (config, { findLoader }) => {
    for (const loader of findLoader('css-loader')) {
      loader.options.modules ??= {}
      loader.options.modules.namedExport = false
      loader.options.modules.exportLocalsConvention = 'as-is'
    }

    for (const loader of findLoader('sass-loader')) {
      loader.options.sassOptions = {
        silenceDeprecations: ['color-functions', 'global-builtin', 'import', 'slash-div', 'if-function']
      }
      loader.options.additionalData = `@import './src/styles/resources.scss';`
    }
  }
}

findLoader() accepts a string or RegExp and returns the actual loader objects, so mutations affect the generated rspack config directly. For adding new rules or plugins, keep editing config itself.

SWC

Jetpack handles app .js, .mjs, .jsx, .ts, and .tsx files through rspack's builtin:swc-loader. Dependency transpilation uses a separate JS-only .js/.mjs rule so npm packages can be compiled for the configured browser target without running TypeScript/JSX parsing across node_modules.

Defaults:

{
  env: {
    targets: /* derived from browserslist + bundle target */,
    coreJs: /* major.minor from installed core-js */,
    mode: /* polyfills option, default 'usage' */
  },
  jsc: {
    externalHelpers: true
  },
  detectSyntax: 'auto',
  isModule: 'unknown'
}

Set polyfills: false to omit coreJs and mode from SWC's env options.

To tweak SWC, find the generated loader options:

export default {
  rspack: (config, { findLoader }) => {
    for (const loader of findLoader('builtin:swc-loader')) {
      loader.options.jsc.parser = { decorators: true }
    }
  }
}

Browserslist

Jetpack uses Browserslist for JS transpilation, CSS syntax lowering, polyfill selection, and modern/legacy serving.

By default, the modern bundle uses baseline widely available with downstream. The legacy bundle uses the Browserslist defaults query.

For the default target: 'modern' build, a regular project Browserslist config customizes the modern bundle:

{
  "browserslist": ["last 2 versions"]
}

When building both bundles with target: 'all', define modern and legacy environments so each bundle has a distinct target. A regular Browserslist config applies to both bundle targets.

{
  "browserslist": {
    "modern": ["baseline widely available with downstream"],
    "legacy": ["defaults"]
  }
}

Hot Reloading

Hot reloading is on by default in development. Disable it with --no-hot, hot: false, or hot: { enabled: false }. Use hot: { quiet: true } to silence browser HMR logs.

CSS hot reloads automatically. React components use fast refresh when React is installed.

For non-React code, accept HMR in your entry module:

if (import.meta.webpackHot) {
  import.meta.webpackHot.accept()
  import.meta.webpackHot.dispose(() => {
    // clean up before the module re-runs
  })
}

Inspect

Use jetpack inspect to build once and write a self-contained treemap to ${build.outDir}/inspect.html.

Chunk Load Retry

Production builds can add a small runtime wrapper that retries failed async chunk loads:

export default {
  build: {
    chunkLoadRetry: {
      maxAttempts: 5,
      base: 1.8,
      multiplier: 500
    }
  }
}

Set build.chunkLoadRetry: true to use those defaults. The retry delay is base ** (attempt - 1) * multiplier milliseconds.