Skip to main content

Migrating to Shakapacker 10.1 Supplemental Packages

Shakapacker 10.1 introduces two optional npm packages — shakapacker-webpack and shakapacker-rspack — that declare the managed-build stack as required peerDependencies. On npm 7+, adopting one of them lets you replace four explicit deps (shakapacker + bundler + CLI + manifest plugin) with a single package. pnpm and Yarn PnP users should keep packages that app config files import directly in devDependencies.

This is opt-in. Apps that don't change anything keep working on 10.1 exactly as they did on 10.0. Adopt at your own pace.

Version pin during the 10.1 rc: the examples below show the GA shape ("~10.1.0"). While 10.1 is still tagged as a release candidate on npm, pin to the published prerelease — e.g. "~10.1.0-rc.1" — so installers can resolve the version from the registry. Switch back to "~10.1.0" after the GA release ships.

Package manager support

The supplemental packages rely on automatic peer dependency installation for the one-command install experience where the package manager makes those peers resolvable from app code:

Package managerWrapper-only install?
npm 7+Yes
pnpmNo — see below
yarn 2+ (Berry) — PnP mode (default)No — see below
yarn 2+ (Berry) — nodeLinker: node-modulesYes
yarn 1 (classic)No — see below

pnpm and Yarn PnP keep dependency boundaries strict: packages imported by your app's config files must be listed directly in your app's package.json. The default generated configs import shakapacker, and many customized webpack configs import webpack, so keep those direct dependencies alongside the supplemental package:

# webpack, pnpm
pnpm add --save-dev shakapacker-webpack shakapacker webpack webpack-cli webpack-assets-manifest terser-webpack-plugin

# webpack, yarn
yarn add --dev shakapacker-webpack shakapacker webpack webpack-cli webpack-assets-manifest terser-webpack-plugin

# rspack, pnpm
pnpm add --save-dev shakapacker-rspack shakapacker @rspack/core @rspack/cli rspack-manifest-plugin

# rspack, yarn
yarn add --dev shakapacker-rspack shakapacker @rspack/core @rspack/cli rspack-manifest-plugin

terser-webpack-plugin is in the webpack list because core shakapacker's default minimizer loads it via require("terser-webpack-plugin") from inside the core package. Under pnpm and Yarn PnP, that require only resolves if the host app declares terser-webpack-plugin directly — shakapacker-webpack's direct dependency on it doesn't reach across the strict package boundary. npm 7+'s flat node_modules hoists it automatically, so npm users don't need to list it.

(The Rails shakapacker:install task handles this automatically — it writes all required deps into your package.json regardless of which package manager you use.)

Why migrate

  • One install command on npm 7+. npm install --save-dev shakapacker-rspack (or shakapacker-webpack) pulls in the required peers automatically. pnpm and Yarn users list direct imports explicitly.
  • No duplicate-bundler risk. Required peer dependencies guarantee one webpack (or @rspack/core) instance in your tree — plugin instanceof checks and shared type references stay coherent.
  • Sets you up for v11. v11 will require the supplemental packages for managed builds (custom-build users keep using bare shakapacker). Adopting now means there's no migration step when v11 lands.

Migration steps

Rspack apps

On npm 7+, replace your bundler-related dev dependencies with a single package. On pnpm or Yarn, use the expanded example below so app-level config imports remain resolvable.

Before (10.0):

{
"devDependencies": {
"shakapacker": "^10.0.0",
"@rspack/core": "^2.0.0",
"@rspack/cli": "^2.0.0",
"rspack-manifest-plugin": "^5.0.0"
}
}

After (10.1+, npm 7+):

{
"devDependencies": {
"shakapacker-rspack": "~10.1.0"
}
}

@rspack/core, @rspack/cli, and rspack-manifest-plugin auto-install as required peers.

After (10.1+, pnpm / yarn):

{
"devDependencies": {
"shakapacker-rspack": "~10.1.0",
"shakapacker": "~10.1.0",
"@rspack/core": "^2.0.0",
"@rspack/cli": "^2.0.0",
"rspack-manifest-plugin": "^5.0.0"
}
}

Run yarn install (or the npm/pnpm equivalent). No Ruby or shakapacker.yml changes are required.

Optional peers — @rspack/plugin-react-refresh, css-loader, sass, sass-loader — stay in your package.json only if your app actually uses those features.

See packages/shakapacker-rspack/README.md for the canonical install reference.

Webpack apps

Before (10.0):

{
"devDependencies": {
"shakapacker": "^10.0.0",
"webpack": "^5.0.0",
"webpack-cli": "^6.0.0",
"webpack-assets-manifest": "^5.0.6"
}
}

After (10.1+, npm 7+):

{
"devDependencies": {
"shakapacker-webpack": "~10.1.0"
}
}

webpack, webpack-cli, and webpack-assets-manifest auto-install as required peers. terser-webpack-plugin rides along as a direct dependency.

After (10.1+, pnpm / yarn):

{
"devDependencies": {
"shakapacker-webpack": "~10.1.0",
"shakapacker": "~10.1.0",
"webpack": "^5.101.0",
"webpack-cli": "^7.0.0",
"webpack-assets-manifest": "^6.0.0",
"terser-webpack-plugin": "^5.3.1"
}
}

Optional peers — transpilers (swc / babel / esbuild), webpack-dev-server, mini-css-extract-plugin, CSS preprocessors, @pmmmwh/react-refresh-webpack-plugin, etc. — stay in your package.json only if your app uses them.

There is one breaking-version note: shakapacker-webpack requires webpack-assets-manifest@^6.0.0. If your app is still on webpack-assets-manifest@5.x, you'll need to upgrade to v6 when adopting shakapacker-webpack. v6 fixed an ENOENT crash on clean builds with merge: true and dropped a Node 14 install path. See the v5→v6 release notes and packages/shakapacker-webpack/README.md for details.

Custom-build apps

If you provide your own webpack/rspack/Vite/etc. setup and only use Shakapacker to read manifest.json, don't install a supplemental package. Continue using bare shakapacker — that's the supported "custom build" path and it doesn't change in v10.1 or v11.

Optional: importing from the supplemental wrapper (pnpm / Yarn PnP)

On pnpm and Yarn PnP, the shakapacker package isn't resolvable from your app code unless you declare it directly — even though the supplemental pulls it in transitively. That's why the "pnpm / yarn" examples above list shakapacker explicitly.

There's a second path that drops shakapacker from your direct deps: change config imports from shakapacker to the supplemental wrapper. The wrappers re-export their core counterparts byte-for-byte (packages/shakapacker-webpack/index.js does module.exports = require("shakapacker")), so this is a name change, not a behavior change:

 // config/webpack/webpack.config.js
-const { generateWebpackConfig } = require("shakapacker")
+const { generateWebpackConfig } = require("shakapacker-webpack")

module.exports = generateWebpackConfig()
 // config/rspack/rspack.config.js (or wherever you import the rspack subpath)
-const { generateRspackConfig } = require("shakapacker/rspack")
+const { generateRspackConfig } = require("shakapacker-rspack")

module.exports = generateRspackConfig()

With that import change, your pnpm/Yarn package.json can drop shakapacker (but still needs the bundler packages and terser-webpack-plugin, since core's default minimizer requires it from inside the core package):

{
"devDependencies": {
"shakapacker-webpack": "~10.1.0",
"webpack": "^5.101.0",
"webpack-cli": "^7.0.0",
"webpack-assets-manifest": "^6.0.0",
"terser-webpack-plugin": "^5.3.1"
}
}

What this path doesn't help with. Any custom webpack/rspack config that imports the bundler directly (e.g. const webpack = require("webpack") to use webpack.DefinePlugin) still needs the bundler declared as a direct dependency on pnpm/Yarn PnP. The supplemental wrapper only re-exports Shakapacker's surface; it doesn't proxy webpack itself. So the wrapper-import path saves you exactly one direct dep (shakapacker) — usually worth doing because it makes the relationship between the supplemental and core explicit, but not a path to a truly minimal devDependencies block.

What you'll see after migration

  • The runtime emits structured warnings (SHAKAPACKER_BUNDLER_MISMATCH, SHAKAPACKER_NO_TRANSPILER) if your config/shakapacker.yml settings disagree with the supplemental package you installed. They surface through Node's built-in warning system, so they're visible in dev and CI but suppressible with --no-warnings.
  • Your existing config/shakapacker.yml, bin/shakapacker, and webpack/rspack configs continue to work without changes.

Need the design rationale?

For the "why this shape" — alternatives considered, comparison to Vite/Next.js, and the v11 roadmap — see docs/dependency-strategy.md.