Skip to main content

SWC Migration Guide

Overview

This document describes the migration from Babel to SWC for JavaScript/TypeScript transpilation in React on Rails projects using Shakapacker 9.0+.

What is SWC?

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.

Prerequisites

  • Shakapacker 9.0+ - SWC support requires Shakapacker version 9.0 or higher
  • Node.js 18+ - Recommended for best compatibility
  • Yarn or npm - For package management

This guide assumes you're already using Shakapacker 9.0+. If you need to upgrade from an earlier version, see the Shakapacker upgrade guide.

Migration Steps

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.

1. Install Required Dependencies

yarn add -D @swc/core swc-loader

2. Update shakapacker.yml

Change the javascript_transpiler setting from babel to swc:

default: &default # Using SWC for faster JavaScript transpilation (20x faster than Babel)
javascript_transpiler: swc

3. Create SWC Configuration File

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;

4. Test the Migration

After configuring SWC, test your build process:

# Compile assets
bundle exec rake shakapacker:compile

# Run tests
bundle exec rspec

React Server Components (RSC) Compatibility

Current Status (2026)

Based on research and testing, here are the key findings regarding SWC and React Server Components compatibility:

⚠️ SWC and RSC: Babel is the tested path

  • Babel is the tested, reference transpiler for RSC in React on Rails. The React on Rails Pro dummy app builds RSC with Babel, so it has the most coverage.
  • The 'use client' / 'use server' boundary transform is performed by the react-on-rails-rsc WebpackLoader, which runs before babel/swc in the loader chain. RSC directive handling does not depend on your transpiler choice — SWC (or Babel) only does ordinary JS/TS transpilation of the RSC, server, and client bundles.
  • Using swc-loader to build an RSC app is wired to work (the generated rscWebpackConfig.js extracts and chains either babel-loader or swc-loader) but is not yet verified end-to-end in React on Rails.
  • The React Compiler's SWC plugin is still experimental, and SWC plugins in general do not follow semver for compatibility. (React Compiler is optional and independent of RSC.)
  • Next.js recommends version 15.3.1+ for optimal SWC-based build performance with RSC.

Known Issues

  1. Plugin Instability: All SWC plugins, including React-related ones, are considered experimental and may have breaking changes without semver guarantees

  2. Framework Dependencies: React Server Components work best with frameworks that have explicit RSC support (like Next.js), as they require build-time infrastructure

  3. Hydration Challenges: When using RSC with SWC, hydration mismatches can occur and are difficult to debug

  4. Library Compatibility: Many popular React libraries are client-centric and may throw hydration errors when used in server components

Recommendations

For Standard React Applications

  • SWC is fully compatible with standard React applications (client-side only)
  • ✅ All 305 React on Rails tests pass with SWC transpilation
  • ✅ Significant performance improvements (20x faster than Babel)

For React Server Components

  • ⚠️ Use with caution - SWC-based RSC builds are not yet the verified path in React on Rails
  • 📝 Document your configuration carefully if using RSC with SWC
  • 🧪 Extensive testing required before production deployment
  • 🔄 Monitor updates to SWC and React Compiler for stability improvements

Alternative: Continue Using Babel for RSC

If you need stable React Server Components support today:

  1. Keep javascript_transpiler: babel in shakapacker.yml
  2. Use the existing Babel configuration with RSC-specific plugins
  3. Wait for SWC RSC support to stabilize before migrating

Migration from Babel to SWC: Feature Comparison

Features Migrated Successfully

Babel FeatureSWC EquivalentNotes
JSX Transformjsc.transform.reactAutomatic runtime supported
React Fast Refreshjsc.transform.react.refreshWorks in development mode
Dynamic Importsjsc.parser.dynamicImportFully supported
Class PropertiesBuilt-inNo config needed
TypeScriptjsc.parser.syntax: 'typescript'Native support

Features Requiring Different Approach

Babel FeatureSWC ApproachMigration Notes
babel-plugin-transform-react-remove-prop-typesBuilt-in optimizationHandled automatically in production
@babel/plugin-proposal-export-default-fromjsc.parser.exportDefaultFromParser option instead of plugin
Babel macrosNot supportedRequires alternative implementation
@loadable/babel-pluginManual code splittingUse React.lazy() instead

Features Not Supported by SWC

  1. Babel Macros - No equivalent, requires code refactoring
  2. Some Babel Plugins - Custom Babel plugins won't work, need alternatives
  3. .swcrc files - Not recommended with webpack; use config/swc.config.js instead

Performance Benefits

Based on testing with React on Rails:

  • Compilation Speed: ~20x faster than Babel
  • Development Experience: Significantly faster HMR (Hot Module Replacement)
  • Build Times: Reduced from minutes to seconds for large applications
  • Memory Usage: Lower memory footprint during builds

Troubleshooting

Issue: PropTypes Not Being Stripped

Solution: SWC automatically strips PropTypes in production mode. Ensure NODE_ENV=production is set.

Issue: CSS Modules Not Working

Solution: CSS Modules handling is done by webpack, not by the transpiler. This should work the same with both Babel and SWC.

Issue: Decorators Not Working

Solution: Enable decorators in SWC config:

jsc: {
parser: {
decorators: true;
}
}

Issue: Class Names Being Mangled (Stimulus)

Solution: Already configured with keepClassNames: true in our SWC config.

Issue: Build Fails with "Cannot find module '@swc/core'"

Solution: Clear node_modules and reinstall:

rm -rf node_modules yarn.lock
yarn install

Issue: Fast Refresh Not Working

Solution: Ensure webpack-dev-server is running and check that:

  • env.runningWebpackDevServer is true in development
  • No syntax errors in components
  • Components follow Fast Refresh rules (no anonymous exports, must export React components)

Issue: Syntax Errors Not Being Caught

Solution: 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

Issue: TypeScript Files Not Transpiling

Solution: For TypeScript files, update your SWC config to use TypeScript parser:

jsc: {
parser: {
syntax: 'typescript',
tsx: true,
dynamicImport: true,
},
// ... rest of config
}

Testing Results

All 305 RSpec tests pass successfully with SWC configuration:

305 examples, 0 failures

Test coverage includes:

  • Client-side rendering
  • Server-side rendering
  • Redux integration
  • React Router
  • CSS Modules
  • Image loading
  • Manual rendering
  • Shared stores

Conclusion

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 builds RSC with Babel, which is the tested transpiler path. SWC-based RSC builds are wired to work but are not yet verified end-to-end, and the React Compiler's SWC plugin remains experimental. Stay with Babel, or conduct extensive testing before production deployment.

References