Use webpack to manage app-like JavaScript modules in Rails
npm install shakapacker---
_π Shakapacker 9 supports Rspack! 10x faster than webpack!_
---
_Official, actively maintained successor to rails/webpacker. ShakaCode stands behind the long-term maintenance and development of this project for the Rails community._
- β οΈ See the 6-stable branch for Shakapacker v6.x code and documentation. :warning:
- See V9 Upgrade for upgrading from the v8 release.
- See V8 Upgrade for upgrading from the v7 release.
- See V7 Upgrade for upgrading from the v6 release.
- See V6 Upgrade for upgrading from v5 or prior v6 releases.







Shakapacker makes it easy to use the JavaScript pre-processor and bundler Webpack v5+
to manage frontend JavaScript in Rails. It can coexist with the asset pipeline,
leaving Webpack responsible solely for frontend JavaScript, or can be used exclusively, making it also responsible for images, fonts, and CSS.
Check out 6.1.1+ for SWC and esbuild-loader support! They are faster than Babel!
See a comparison of Shakapacker with jsbundling-rails. For an in-depth discussion of choosing between shakapacker and jsbundling-rails, see the discussion Webpacker alternatives - which path should we go to? #8783 and the resulting PR Switch away from Webpacker to Shakapacker #10389.
For discussions, see our Slack Channel.
---
ShakaCode focuses on helping Ruby on Rails teams use React and Webpack better. We can upgrade your project and improve your development and customer experiences, allowing you to focus on building new features or fixing bugs instead.
For an overview of working with us, see our Client Engagement Model article and how we bill for time.
We also specialize in helping development teams lower infrastructure and CI costs. Check out our project Control Plane Flow, which can allow you to get the ease of Heroku with the power of Kubernetes and big cost savings.
If you think ShakaCode can help your project, click here to book a call with Justin Gordon, the creator of React on Rails and Shakapacker.
Here's a testimonial of how ShakaCode can help from Florian GΓΆΓler of Blinkist, January 2, 2023:
> Hey Justin π
>
> I just wanted to let you know that we today shipped the webpacker to shakapacker upgrades and it all seems to be running smoothly! Thanks again for all your support and your teams work! π
>
> On top of your work, it was now also very easy for me to upgrade Tailwind and include our external node_module based web component library which we were using for our other (more modern) apps already. That work is going to be shipped later this week though as we are polishing the last bits of it. π
>
> Have a great 2023 and maybe we get to work together again later in the year! π
Read the full review here.
---
- Prerequisites
- Features
- Optional support
- Installation
- Rails v6+
- Concepts
- Usage
- Configuration and Code
- Configuration Guide
- View Helpers
- View Helpers javascript_pack_tag and stylesheet_pack_tag
- View Helpers append_javascript_pack_tag, prepend_javascript_pack_tag and append_stylesheet_pack_tag
- View Helper: asset_pack_path
- View Helper: image_pack_tag
- View Helper: favicon_pack_tag
- View Helper: preload_pack_asset
- View Helper: send_pack_early_hints
- Images in Stylesheets
- Server-Side Rendering (SSR)
- Development
- Automatic Webpack Code Building
- Compiler strategies
- Common Development Commands
- Ruby API Reference
- Webpack Configuration
- Babel configuration
- SWC configuration
- esbuild loader configuration
- Integrations
- React
- Typescript
- CSS
- Postcss
- Sass
- Less
- Stylus
- CoffeeScript
- Other frameworks
- Custom Rails environments
- Upgrading
- Paths
- Additional paths
- Deployment
- Example Apps
- Troubleshooting
- Contributing
- License
- Supporters
- Ruby 2.7+
- Rails 5.2+
- Node.js 14+
- Rails view helpers that fully support Webpack output, including HMR and code splitting.
- Convenient but not required webpack configuration. The only requirement is that your webpack configuration creates a manifest.
- HMR with the shakapacker-dev-server, such as for hot-reloading React!
- Automatic code splitting using multiple entry points to optimize JavaScript downloads.
- Support for NPM, Yarn (classic and berry), PNPM, and Bun
- Webpack v5+
- ES6 with babel, SWC, or Esbuild
- Asset compression, source-maps, and minification
- CDN support
- Extensible and configurable. For example, all major dependencies are specified as peers, so you can upgrade easily.
_Requires extra packages to be installed._
- React
- TypeScript
- Stylesheets - Sass, Less, Stylus and Css, PostCSS
- CoffeeScript
With Rails v6+, skip JavaScript for a new app and follow below Manual Installation Steps to manually add the shakapacker gem to your Gemfile.
``bash`
rails new myapp --skip-javascript
_Note, Rails 6 installs the older v5 version of webpacker unless you specify --skip-javascript._
Add shakapacker gem to your Gemfile:
`bash`
bundle add shakapacker --strict
Then run the following to install Shakapacker:
`bash`
./bin/bundle install
bundle exec rake shakapacker:install
Before initiating the installation process, ensure you have committed all the changes. While installing Shakapacker, there might be some conflict between the existing file content and what Shakapacker tries to copy. You can either approve all the prompts for overriding these files or use the FORCE=true environment variable before the installation command to force the override without any prompt.
Shakapacker uses the package_json gem to handle updating the package.json and interacting with the underlying package manager of choice for managing dependencies and running commands; the package manager is managed using the packageManager property in the package.json, otherwise falling back to the value of PACKAGE_JSON_FALLBACK_MANAGER if set or otherwise npm.
If packageManager is not set when running shakapacker:install, Shakapacker will set it based on the lockfile and the result of calling --version on the inferred manager; if no lockfile is present, then npm be used unless you choose to explicitly set the PACKAGE_JSON_FALLBACK_MANAGER to your preferred package manager.
> [!NOTE]
>
> The packageManager property is only used to determine the package manager to use, based primarily on its name.corepack
> The version (if present) is only used to determine if Yarn Classic or Yarn Berry should be used, but is otherwise
> _not_ checked, nor is used to ensure that the package manager is installed.corepack
>
> It is up to the developer to ensure that the desired package manager is actually install at the right version, which can be done
> using or by other means.
See here for a list of the supported package managers and more information; note that package_json does not handle ensuring the manager is installed.
If you wish to use Yarn PnP you will need to configure Babel using a babel.config.js file rather than via package.json - see customizing Babel Config for examples on how to do this.
> [!NOTE]
>
> The rest of the documentation will only reference npm when providing commands such as to install optional packages except in cases wherenpm
> a particular package manager requires a very different command; otherwise it should be safe to just replace with the name of your
> preferred package manager when running the command
Note, in v6+, most JS packages are peer dependencies. Thus, the installer will add the packages:
- @babel/core@babel/plugin-transform-runtime
- @babel/preset-env
- @babel/runtime
- babel-loader
- compression-webpack-plugin
- terser-webpack-plugin
- webpack
- webpack-assets-manifest
- webpack-cli
- webpack-merge
- webpack-sources
- webpack-dev-server
-
Previously, these "webpack" and "babel" packages were direct dependencies for shakapacker. By
making these peer dependencies, you have control over the versions used in your webpack and babel configs.
All peer dependencies in Shakapacker are marked as optional via peerDependenciesMeta. This design decision ensures:
- No warnings during package installation when dependencies are not needed
- Clear visibility of supported package versions for upgrades
- Flexibility to choose only the tools you need (webpack vs rspack, babel vs swc vs esbuild)
The optional peer dependencies approach means you only install what you actually use, while still maintaining
version compatibility constraints when you do install those packages.
#### Required Dependencies by Configuration
Depending on your setup, you'll need different subsets of the optional peer dependencies:
For Webpack + Babel (traditional setup):
`json`
{
"dependencies": {
"shakapacker": "^9.0.0",
"@babel/core": "^7.17.9",
"@babel/plugin-transform-runtime": "^7.17.0",
"@babel/preset-env": "^7.16.11",
"@babel/runtime": "^7.17.9",
"babel-loader": "^8.2.4",
"compression-webpack-plugin": "^9.0.0",
"terser-webpack-plugin": "^5.3.1",
"webpack": "^5.76.0",
"webpack-assets-manifest": "^5.0.6",
"webpack-cli": "^5.0.0",
"webpack-dev-server": "^5.0.0"
}
}
For Webpack + SWC (faster alternative):
`json`
{
"dependencies": {
"shakapacker": "^9.0.0",
"@swc/core": "^1.3.0",
"swc-loader": "^0.2.0",
"compression-webpack-plugin": "^9.0.0",
"terser-webpack-plugin": "^5.3.1",
"webpack": "^5.76.0",
"webpack-assets-manifest": "^5.0.6",
"webpack-cli": "^5.0.0",
"webpack-dev-server": "^5.0.0"
}
}
For Rspack + SWC (10x faster bundling):
`json`
{
"dependencies": {
"shakapacker": "^9.0.0",
"@rspack/core": "^1.0.0",
"@rspack/cli": "^1.0.0",
"@swc/core": "^1.3.0",
"swc-loader": "^0.2.0",
"rspack-manifest-plugin": "^5.0.0"
}
}
Quick tip: You can easily switch between webpack and rspack using:
`bash
bundle exec rake shakapacker:switch_bundler rspack -- --install-deps
See the Rspack Migration Guide for details.
For CSS/Sass processing (add to any config above):
`json
{
"dependencies": {
"css-loader": "^6.8.1",
"mini-css-extract-plugin": "^2.0.0",
"sass": "^1.50.0",
"sass-loader": "^13.0.0"
}
}
`Concepts
At its core, Shakapacker's essential function is to:
1. Provide configuration by a single file used by both Rails view helpers and JavaScript webpack compilation code.
2. Provide Rails view helpers, utilizing this configuration file so that a webpage can load JavaScript, CSS, and other static assets compiled by webpack, supporting bundle splitting, fingerprinting, and HMR.
3. Provide a community-supported, default webpack compilation that generates the necessary bundles and manifest, using the same configuration file. This compilation can be extended for any needs.
Usage
$3
π For a comprehensive guide to all configuration options, see the Configuration Guide
This includes documentation for:
- All
config/shakapacker.yml options (including assets_bundler_config_path)
- Environment-specific configuration
- Development server settings
- Build configurations (config/shakapacker-builds.yml)
- Best practices and common patternsYou will need your file system to correspond to the setup of your
config/shakapacker.yml file.Suppose you have the following configuration:
shakapacker.yml`yml
default: &default
source_path: app/javascript
source_entry_path: packs
public_root_path: public
public_output_path: packs
nested_entries: false
And more
`And that maps to a directory structure like this:
`
app/javascript:
βββ packs: # sets up webpack entries
β βββ application.js # references ../src/my_component.js
β βββ application.css
βββ src: # any directory name is fine. Referenced files need to be under source_path
β βββ my_component.js
βββ stylesheets:
β βββ my_styles.css
βββ images:
βββ logo.svg
public/packs # webpack output
`Webpack intelligently includes only necessary files. In this example, the file
packs/application.js would reference ../src/my_component.jsThe
nested_entries option allows webpack entry points in subdirectories (defaults to true). See the Configuration Guide for details.The
useContentHash option enables content-based cache busting. It's disabled by default (except in production) to speed up development builds. See the Configuration Guide for details.#### Precompile Hook
Shakapacker supports running custom commands before compilation via the
precompile_hook configuration option.For configuration details, see precompile_hook in the Configuration Guide.
For complete usage guide, see the Precompile Hook Guide.
#### Setting custom config path
You can use the
SHAKAPACKER_CONFIG environment variable to specify a custom config file path. See Environment Variables in the Configuration Guide for this and other configuration options.$3
The Shakapacker view helpers generate the script and link tags to get the webpack output onto your views.
Be sure to consult the API documentation in the source code of helper.rb.
Note: For your styles or static assets files to be available in your view, you would need to link them in your "pack" or entry file. Otherwise, Webpack won't know to package up those files.
#### View Helpers
javascript_pack_tag and stylesheet_pack_tagThese view helpers take your
shakapacker.yml configuration file and the resulting webpack compilation manifest.json and generate the HTML to load the assets.You can then link the JavaScript pack in Rails views using the
javascript_pack_tag helper. If you have styles imported in your pack file, you can link them by using stylesheet_pack_tag:`erb
<%= javascript_pack_tag 'application' %>
<%= stylesheet_pack_tag 'application' %>
`The
javascript_pack_tag and stylesheet_pack_tag helpers will include all the transpiled
packs with the chunks in your view, which creates HTML tags for all the chunks.You can provide multiple packs and other attributes. Note,
defer defaults to showing.`erb
<%= javascript_pack_tag 'calendar', 'map', 'data-turbo-track': 'reload' %>
`The resulting HTML would look like this:
`html
`In this output, both the calendar and map codes might refer to other common libraries. Those get placed in something like the vendor bundle. The view helper removes any duplication.
Note, the default of "defer" for the
javascript_pack_tag. You can override that to false. If you expose jquery globally with expose-loader, by using import $ from "expose-loader?exposes=$,jQuery!jquery" in your app/javascript/application.js, pass the option defer: false to your javascript_pack_tag.The
javascript_pack_tag also supports the async attribute, which you can enable by passing async: true:`erb
<%= javascript_pack_tag 'application', async: true %>
`This will generate script tags with the
async attribute, which allows the browser to download and execute the script asynchronously without blocking HTML parsing:`html
`Note that when using
async: true, scripts may execute in any order as soon as they're downloaded, which could cause issues if your code has dependencies between files. In most cases, defer (the default) is preferred as it maintains execution order.> [!NOTE]
>
> When both
async and defer attributes are specified, async takes precedence according to HTML5 specifications. So if you pass both async: true and defer: true, the script tag will use async.Important: Pass all your pack names as multiple arguments, not multiple calls, when using
javascript_pack_tag and the stylesheet_pack_tag. Otherwise, you will get duplicated chunks on the page.`erb
<%# DO %>
<%= javascript_pack_tag 'calendar', 'map' %><%# DON'T %>
<%= javascript_pack_tag 'calendar' %>
<%= javascript_pack_tag 'map' %>
`While this also generally applies to
stylesheet_pack_tag,
you may use multiple calls to stylesheet_pack_tag if,
say,
you require multiple