Skip to main content

Common Shakapacker Upgrade Guides

This document provides step-by-step instructions for the most common upgrade scenarios in Shakapacker projects.

📖 For configuration options, see the Configuration Guide

Table of Contents


Upgrading Shakapacker

⚠️ Important: Shakapacker is both a Ruby gem AND an npm package. You must update BOTH when upgrading.

Shakapacker consists of two components that must be updated together:

  1. Ruby gem - provides Rails integration and view helpers
  2. npm package - provides webpack/rspack configuration and build tools

Upgrade Steps

1. Update Gemfile

gem "shakapacker", "10.0.0"  # or the version you want to upgrade to

Pre-release versions: Ruby gems use dot notation (e.g., "10.0.0.beta.1")

2. Update package.json

{
"dependencies": {
"shakapacker": "10.0.0"
}
}

Pre-release versions: npm uses hyphen notation (e.g., "10.0.0-beta.1")

3. Run bundler and package manager

bundle update shakapacker
yarn install # or npm install, pnpm install, bun install

4. Test your build

bin/shakapacker
bin/shakapacker-dev-server

Why Both Must Be Updated

  • Mismatched versions can cause build failures - The Ruby gem expects specific configuration formats from the npm package
  • Feature compatibility - New features in the gem require corresponding npm package updates
  • Bug fixes - Fixes often span both Ruby and JavaScript code

Version Format Differences

Note that pre-release versions use different formats:

ComponentStable VersionPre-release Version
Gemfile"10.0.0""10.0.0.beta.1"
package.json"10.0.0""10.0.0-beta.1"

Finding the Latest Version

Major Version Upgrades

For major version upgrades, always consult the version-specific upgrade guides for breaking changes and new features:

💡 Note: Major version upgrades may include breaking changes. The steps above cover the basic gem/package updates that apply to all versions, but you should always review the version-specific guide for additional migration steps.


Adopting Supplemental Packages (10.1+)

Shakapacker 10.1 introduces two optional npm packages — shakapacker-webpack and shakapacker-rspack — that bundle the managed-build stack as direct dependencies. Adopting one of them lets you replace four explicit devDependencies (shakapacker + bundler + CLI + manifest plugin) with a single package that lockstep-pins to the exact versions Shakapacker is tested against.

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

Rspack apps can replace shakapacker + @rspack/core + @rspack/cli + rspack-manifest-plugin with a single shakapacker-rspack dev dependency.

Webpack apps can replace shakapacker + webpack + webpack-cli + webpack-assets-manifest with a single shakapacker-webpack dev dependency. One caveat: shakapacker-webpack pins webpack-assets-manifest to ~6.5.1, so apps still on webpack-assets-manifest@5.x need to upgrade to v6 when adopting it.

Custom-build apps (apps that ship their own webpack/rspack/Vite setup and only use Shakapacker to read manifest.json) should not install a supplemental package — continue using bare shakapacker.

See the v10.1 supplemental packages migration guide for before/after package.json snippets, the webpack-assets-manifest v5→v6 upgrade notes, and the v11 roadmap context. The per-package install references live at packages/shakapacker-webpack/README.md and packages/shakapacker-rspack/README.md.


Automating Updates with Dependabot

Because Shakapacker ships as both a Ruby gem and an npm package, both sides must be bumped together. Dependabot's multi-ecosystem groups can open a single PR that updates both ecosystems at once.

See Dependabot configuration for Shakapacker for an example .github/dependabot.yml that keeps the gem and npm package in sync.


Migrating Package Managers

Yarn to npm

Migrating from Yarn to npm is straightforward as both use similar package management concepts.

1. Remove Yarn lock file

rm yarn.lock

2. Install dependencies with npm

npm install

This will create a new package-lock.json file.

3. Update scripts (if necessary)

Review your package.json scripts and any deployment scripts that may reference yarn commands. Replace them with npm equivalents:

{
"scripts": {
"build": "shakapacker",
"dev": "shakapacker-dev-server"
}
}

4. Update CI/CD pipelines

If you have CI/CD pipelines, update them to use npm install instead of yarn install.

5. Test your build

npm run build

Common npm equivalents

Yarn Commandnpm Equivalent
yarnnpm install
yarn add <package>npm install <package>
yarn add --dev <package>npm install --save-dev <package>
yarn remove <package>npm uninstall <package>
yarn run <script>npm run <script>

npm to Yarn

Converting from npm to Yarn is equally straightforward.

1. Remove npm lock file

rm package-lock.json

2. Install Yarn (if not already installed)

Use Corepack (recommended for modern Node.js):

corepack enable
corepack prepare yarn@stable --activate

Alternative - install globally via npm:

npm install -g yarn

3. Install dependencies with Yarn

yarn install

This will create a new yarn.lock file.

4. Update scripts

Yarn can run scripts directly without the run command:

# Both work with Yarn
yarn build
yarn run build

5. Test your build

yarn build

Migrating to pnpm

pnpm is a fast, disk space-efficient package manager that's gaining popularity.

1. Install pnpm

Use Corepack (recommended):

corepack enable
corepack prepare pnpm@latest --activate

Alternative - install globally via npm:

npm install -g pnpm

2. Remove existing lock files

rm yarn.lock package-lock.json

3. Install dependencies with pnpm

pnpm install

This creates a pnpm-lock.yaml file.

4. Update scripts (if necessary)

pnpm uses the same script syntax as npm/Yarn:

pnpm run build
pnpm dev

5. Configure pnpm workspace (optional)

If you have a monorepo setup, create a pnpm-workspace.yaml:

packages:
- "packages/*"

6. Test your build

pnpm run build

pnpm benefits

  • Faster installs: Uses pnpm's content-addressable store and hard links to speed up repeat installs
  • Disk space efficient: Uses hard links to save disk space
  • Strict: Better at catching dependency issues

Migrating from Babel to SWC

SWC is a high-performance Rust-based JavaScript/TypeScript compiler. SWC reports being 20x faster than Babel on a single thread and 70x faster on four cores in its own transpiler benchmark; the practical speedup on a full Shakapacker build is usually smaller but still substantial. For complete details, see JavaScript Transpiler Configuration.

Quick Migration Steps

1. Install SWC dependencies

# Using Yarn
yarn add --dev @swc/core swc-loader

# Using npm
npm install --save-dev @swc/core swc-loader

# Using pnpm
pnpm add --save-dev @swc/core swc-loader

2. Update shakapacker.yml

# config/shakapacker.yml
default: &default
javascript_transpiler: swc
bundle exec rake shakapacker:migrate_to_swc

This will automatically create a config/swc.config.js with sensible defaults, including Stimulus compatibility if needed.

4. Create SWC configuration (if not using rake task)

If you're configuring manually, create config/swc.config.js:

// config/swc.config.js
// This file is merged with Shakapacker's default SWC configuration
// See: https://swc.rs/docs/configuration/compilation

module.exports = {
options: {
jsc: {
// CRITICAL for Stimulus compatibility: Prevents SWC from mangling class names
keepClassNames: true,
transform: {
react: {
runtime: "automatic"
}
}
}
}
}

Note: The options wrapper is required for proper merging with Shakapacker's defaults. Using .swcrc instead will completely override Shakapacker's settings and may cause build failures.

5. Update React refresh plugin (if using React)

# For webpack
yarn add --dev @pmmmwh/react-refresh-webpack-plugin

# For rspack
yarn add --dev @rspack/plugin-react-refresh

6. Test your build

bin/shakapacker

7. Run your test suite

# Ensure everything works as expected
bundle exec rspec

Performance Expectations

SWC's own benchmark reports being 20x faster than Babel single-threaded and 70x faster on four cores for pure transpilation. A real Shakapacker build also runs CSS processing, minification, source map generation, and your plugin chain, so the end-to-end speedup is typically smaller than the transpiler number in isolation — but the transpiler is usually the dominant cost on Babel-heavy projects, so the move is almost always a large win.

Exact gains depend on project size, configuration, source maps, cache state, hardware, and whether you measure only transpilation or the full Shakapacker build. See Measuring Your App for how to confirm the gain on your codebase.

Common Issues

Issue: Decorators not working

Add decorator support to config/swc.config.js:

module.exports = {
options: {
jsc: {
parser: {
decorators: true,
decoratorsBeforeExport: true
}
}
}
}

Issue: Stimulus controllers not working

Ensure keepClassNames: true is set in config/swc.config.js.

Rollback

If you need to revert:

# config/shakapacker.yml
default: &default
javascript_transpiler: babel

Then rebuild:

bundle exec rake shakapacker:clobber
bundle exec rake shakapacker:compile

Migrating from Webpack to Rspack

Rspack is a high-performance bundler written in Rust with excellent webpack compatibility. Rspack's published benchmark on a 5,000-component React app reports roughly 8x faster production builds, 10–15x faster development startup, and 17x faster HMR vs webpack (rspack.rs, benchmark sources). For complete details, see Rspack Migration Guide.

Quick Migration Steps

Shakapacker provides a convenient rake task to automate the migration:

# Switch to rspack with automatic dependency management (note the -- separator)
bin/rake shakapacker:switch_bundler rspack -- --install-deps

# Fast switching without uninstalling webpack (keeps both)
bin/rake shakapacker:switch_bundler rspack -- --install-deps --no-uninstall

⚠️ Important: This task must be run with bin/rake, not bin/rails.

The task will:

  • Update config/shakapacker.yml to use rspack
  • Install rspack dependencies (with --install-deps)
  • Optionally uninstall webpack dependencies (default) or keep both (with --no-uninstall)
  • Update javascript_transpiler to swc (recommended for rspack)
  • Preserve your config file comments and structure

Custom dependencies: You can customize which dependencies are installed:

bin/rake shakapacker:switch_bundler -- --init-config

2. Manual installation (alternative)

If you prefer manual control:

# Install Rspack dependencies
# Using Yarn
yarn add --dev @rspack/core @rspack/cli

# Using npm
npm install --save-dev @rspack/core @rspack/cli

# Using pnpm
pnpm add --save-dev @rspack/core @rspack/cli

# Remove webpack dependencies (optional)
yarn remove webpack webpack-cli webpack-dev-server
# Or: npm uninstall webpack webpack-cli webpack-dev-server
# Or: pnpm remove webpack webpack-cli webpack-dev-server

Then update config/shakapacker.yml:

default: &default
assets_bundler: rspack
javascript_transpiler: swc # Rspack defaults to SWC for best performance

3. Create Rspack configuration

Create config/rspack/rspack.config.js based on your webpack config. Start with a minimal configuration:

// config/rspack/rspack.config.js
const { rspack } = require("@rspack/core")
const { merge } = require("webpack-merge")
const baseConfig = require("../shakapacker")

module.exports = merge(baseConfig, {
module: {
rules: [
{
test: /\.(js|jsx|ts|tsx)$/,
loader: "builtin:swc-loader",
options: {
jsc: {
parser: {
syntax: "ecmascript",
jsx: true
},
transform: {
react: {
runtime: "automatic"
}
}
}
}
}
]
}
})

4. Update TypeScript configuration

Add isolatedModules: true to your tsconfig.json:

{
"compilerOptions": {
"isolatedModules": true
}
}

5. Replace incompatible plugins

Some webpack plugins need Rspack equivalents:

Webpack PluginRspack Alternative
mini-css-extract-pluginrspack.CssExtractRspackPlugin
copy-webpack-pluginrspack.CopyRspackPlugin
terser-webpack-pluginrspack.SwcJsMinimizerRspackPlugin
fork-ts-checker-webpack-plugints-checker-rspack-plugin
@pmmmwh/react-refresh-webpack-plugin@rspack/plugin-react-refresh

Example plugin update:

// Before (webpack)
const MiniCssExtractPlugin = require("mini-css-extract-plugin")
plugins: [new MiniCssExtractPlugin()]

// After (rspack)
const { rspack } = require("@rspack/core")
plugins: [new rspack.CssExtractRspackPlugin()]

6. Update asset handling

Replace file loaders with asset modules:

// Before (webpack with file-loader)
{
test: /\.(png|jpg|gif)$/,
use: ['file-loader']
}

// After (rspack with asset modules)
{
test: /\.(png|jpg|gif)$/,
type: 'asset/resource'
}

7. Install React refresh plugin (if using React)

yarn add --dev @rspack/plugin-react-refresh

Update your config:

const { ReactRefreshRspackPlugin } = require("@rspack/plugin-react-refresh")
const { rspack } = require("@rspack/core")

module.exports = {
plugins: [
new ReactRefreshRspackPlugin(),
new rspack.HotModuleReplacementPlugin()
]
}

8. Test your build

# Development build
bin/shakapacker

# Production build
bin/shakapacker --mode production

9. Update development workflow

Rspack's dev server works the same way:

bin/shakapacker-dev-server

Migration Checklist

  • Install Rspack dependencies
  • Update config/shakapacker.yml
  • Create config/rspack/rspack.config.js
  • Replace incompatible plugins
  • Update TypeScript config (add isolatedModules: true)
  • Convert file loaders to asset modules
  • Test development build
  • Test production build
  • Run test suite
  • Update CI/CD pipelines
  • Deploy to staging
  • Monitor performance improvements

Performance Benefits

Rspack's own published benchmark on a 5,000-component React app reports:

WorkloadWebpack 5RspackApprox. speedup
Cold development start~8.2s~0.7s~10x
Cold production build~9.5s~1.6s~6x
HMR update~2.8s~160ms~17x

Source: rspack.rs and rstackjs/build-tools-performance (react-5k case).

Real Shakapacker apps will land somewhere in this range depending on project size, configuration, source maps, cache state, and hardware. Measure your real build and development workflows to confirm the gain on your app — see Measuring Your App.

Common Issues

Issue: LimitChunkCountPlugin Error

Error: Cannot read properties of undefined (reading 'tap')

Solution: Remove webpack.optimize.LimitChunkCountPlugin and use splitChunks configuration instead.

Issue: CSS not extracting

Solution: Use rspack.CssExtractRspackPlugin instead of mini-css-extract-plugin.

Issue: TypeScript errors

Solution: Ensure isolatedModules: true is set in tsconfig.json.

Rollback

If you need to revert to webpack:

# config/shakapacker.yml
default: &default
assets_bundler: webpack
javascript_transpiler: babel # or swc

Then rebuild:

bundle exec rake shakapacker:clobber
bundle exec rake shakapacker:compile

Combined Migration Path

For maximum performance improvements, you can combine multiple migrations:

This combination provides the largest end-to-end build performance improvement available in Shakapacker: SWC handles transpilation in Rust (upstream reports up to 20x/70x vs Babel) and Rspack handles the rest of the bundler pipeline in Rust (upstream reports roughly 8–17x vs webpack on its own benchmark). Apply them in order so you can attribute any regression to the right layer:

  1. First, migrate to SWC (while still on webpack)

  2. Then, migrate to Rspack

Alternative: Webpack + Babel → Rspack + SWC (all at once)

If you're confident, you can do both migrations simultaneously:

# config/shakapacker.yml
default: &default
assets_bundler: rspack
javascript_transpiler: swc

Follow both migration guides, installing all required dependencies at once.


Getting Help