docs/oss/migrating/babel-to-swc-migration.md
This document describes the migration from Babel to SWC for JavaScript/TypeScript transpilation in React on Rails projects using Shakapacker 9.0+.
SWC (Speedy Web Compiler) is a Rust-based JavaScript/TypeScript compiler that is approximately 20x faster than Babel. Shakapacker 9.0+ uses SWC as the default transpiler.
This guide assumes you're already using Shakapacker 9.0+. If you need to upgrade from an earlier version, see the Shakapacker upgrade guide.
Note: This migration has been successfully implemented in the React on Rails standard dummy app (react_on_rails/spec/dummy). The Pro dummy app (react_on_rails_pro/spec/dummy) continues using Babel for RSC stability.
yarn add -D @swc/core swc-loader
Change the javascript_transpiler setting from babel to swc:
default: &default # Using SWC for faster JavaScript transpilation (20x faster than Babel)
javascript_transpiler: swc
Create config/swc.config.js in your Rails application root with the following content:
let env;
try {
({ env } = require('shakapacker'));
} catch (error) {
console.error('Failed to load shakapacker:', error.message);
console.error('Make sure shakapacker is installed: yarn add shakapacker');
process.exit(1);
}
const customConfig = {
options: {
jsc: {
parser: {
syntax: 'ecmascript',
jsx: true,
dynamicImport: true,
},
transform: {
react: {
runtime: 'automatic',
development: env.isDevelopment,
refresh: env.isDevelopment && env.runningWebpackDevServer,
useBuiltins: true,
},
},
// Keep class names for better debugging and compatibility
keepClassNames: true,
},
env: {
targets: '> 0.25%, not dead',
},
},
};
module.exports = customConfig;
After configuring SWC, test your build process:
# Compile assets
bundle exec rake shakapacker:compile
# Run tests
bundle exec rspec
Based on research and testing, here are the key findings regarding SWC and React Server Components compatibility:
Plugin Instability: All SWC plugins, including React-related ones, are considered experimental and may have breaking changes without semver guarantees
Framework Dependencies: React Server Components work best with frameworks that have explicit RSC support (like Next.js), as they require build-time infrastructure
Hydration Challenges: When using RSC with SWC, hydration mismatches can occur and are difficult to debug
Library Compatibility: Many popular React libraries are client-centric and may throw hydration errors when used in server components
If you need stable React Server Components support today:
javascript_transpiler: babel in shakapacker.yml| Babel Feature | SWC Equivalent | Notes |
|---|---|---|
| JSX Transform | jsc.transform.react | Automatic runtime supported |
| React Fast Refresh | jsc.transform.react.refresh | Works in development mode |
| Dynamic Imports | jsc.parser.dynamicImport | Fully supported |
| Class Properties | Built-in | No config needed |
| TypeScript | jsc.parser.syntax: 'typescript' | Native support |
| Babel Feature | SWC Approach | Migration Notes |
|---|---|---|
babel-plugin-transform-react-remove-prop-types | Built-in optimization | Handled automatically in production |
@babel/plugin-proposal-export-default-from | jsc.parser.exportDefaultFrom | Parser option instead of plugin |
| Babel macros | Not supported | Requires alternative implementation |
@loadable/babel-plugin | Manual code splitting | Use React.lazy() instead |
.swcrc files - Not recommended with webpack; use config/swc.config.js insteadBased on testing with React on Rails:
Solution: SWC automatically strips PropTypes in production mode. Ensure NODE_ENV=production is set.
Solution: CSS Modules handling is done by webpack, not by the transpiler. This should work the same with both Babel and SWC.
Solution: Enable decorators in SWC config:
jsc: {
parser: {
decorators: true;
}
}
Solution: Already configured with keepClassNames: true in our SWC config.
Solution: Clear node_modules and reinstall:
rm -rf node_modules yarn.lock
yarn install
Solution: Ensure webpack-dev-server is running and check that:
env.runningWebpackDevServer is true in developmentSolution: SWC parser is more permissive than Babel. Add TypeScript or stricter ESLint configuration for better error catching:
yarn add -D @typescript-eslint/parser @typescript-eslint/eslint-plugin
Solution: For TypeScript files, update your SWC config to use TypeScript parser:
jsc: {
parser: {
syntax: 'typescript',
tsx: true,
dynamicImport: true,
},
// ... rest of config
}
All 305 RSpec tests pass successfully with SWC configuration:
305 examples, 0 failures
Test coverage includes:
For React on Rails projects without React Server Components: ✅ Migration to SWC is recommended
The standard React on Rails dummy app (react_on_rails/spec/dummy) successfully uses SWC, demonstrating its compatibility with core React on Rails features.
For projects using React Server Components: ⚠️ Stay with Babel for now - The React on Rails Pro dummy app continues using Babel due to RSC's experimental status with SWC. Consider staying with Babel until SWC RSC support stabilizes, or conduct extensive testing before production deployment.