Skip to content

Comments

@remotion/bundler: Add experimental flag for Rspack (Fast Refresh not yet working)#6596

Merged
JonnyBurger merged 17 commits intomainfrom
rspack-bundler-option
Feb 20, 2026
Merged

@remotion/bundler: Add experimental flag for Rspack (Fast Refresh not yet working)#6596
JonnyBurger merged 17 commits intomainfrom
rspack-bundler-option

Conversation

@JonnyBurger
Copy link
Member

@JonnyBurger JonnyBurger commented Feb 19, 2026

See #6608 for progress regarding Rspack

@vercel
Copy link
Contributor

vercel bot commented Feb 19, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
bugs Ready Ready Preview, Comment Feb 20, 2026 9:38am
remotion Ready Ready Preview, Comment Feb 20, 2026 9:38am

Request Review

Add Rspack as an optional alternative bundler controlled by `--rspack` flag
(e.g., `npx remotion studio --rspack`). Webpack remains the default.

- Add `@rspack/core` and `@rspack/plugin-react-refresh` dependencies
- Create `rspack-config.ts` paralleling webpack-config.ts with SWC loader
- Define `--rspack` CLI option and thread it through studio/bundle paths
- Branch compiler creation in start-server.ts and bundle.ts based on flag

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…imentalRspackEnabled()`

Mark the Rspack bundler flag as experimental, add a config setter,
warn when enabled, add template validation test, and document the new option.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…eploySite` + docs

Thread the `--experimental-rspack` CLI flag through the entire bundle/render pipeline instead of hardcoding `false`. Add `rspack?: boolean` to Lambda and CloudRun `deploySite()` options. Document the new option across all relevant CLI commands and Node.js APIs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…-update files

- Delete process-update.ts which had complex recursive HMR check/apply logic
  that interfered with rspack's native React Fast Refresh
- Replace with minimal inline module.hot.check(true) in client.ts with
  hash comparison and idle status guards
- Add disk fallback in dev middleware for .hot-update. files that rspack's
  native compiler may write to disk instead of memfs
- Disable ReactRefreshPlugin overlay for rspack

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@JonnyBurger JonnyBurger mentioned this pull request Feb 20, 2026
21 tasks
@JonnyBurger JonnyBurger changed the title @remotion/bundler: Add --experimental-rspack flag for optional Rspack bundling @remotion/bundler: Add experimental flag for Rspack (Fast Refresh not yet working) Feb 20, 2026
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Contributor

@pullfrog pullfrog bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Low urgency. Well-structured experimental feature with proper gating. Found one bug in the option state management. The abstraction layer between webpack and rspack is clean, though users with custom webpackOverride functions may encounter issues since the same override is applied to both bundlers. The acknowledgment in the title about Fast Refresh not working sets appropriate expectations.

Pullfrog  | Fix all ➔Fix 👍s ➔View workflow runpullfrog.com𝕏

Comment on lines +17 to +18
if (commandLine[cliFlag] !== undefined) {
rspackEnabled = true;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: When the CLI flag is explicitly set to false (e.g., --experimental-rspack=false), this still sets rspackEnabled = true in global state. Subsequent calls without the CLI flag will then incorrectly return true.

Should be:

rspackEnabled = commandLine[cliFlag] as boolean;
Suggested change
if (commandLine[cliFlag] !== undefined) {
rspackEnabled = true;
if (commandLine[cliFlag] !== undefined) {
rspackEnabled = commandLine[cliFlag] as boolean;

@JonnyBurger JonnyBurger merged commit 9e8e74d into main Feb 20, 2026
20 checks passed
@JonnyBurger JonnyBurger deleted the rspack-bundler-option branch February 20, 2026 09:41
Copy link
Contributor

@pullfrog pullfrog bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clean refactoring that extracts shared bundler config and adds an experimental rspack path. The approach is sound — shared config pieces in shared-bundler-config.ts, parallel rspack config, and consistent plumbing through CLI/API layers. A few items worth considering below, mostly around dependency strategy and a minor bug in the option definition.

Pullfrog  | Fix all ➔Fix 👍s ➔View workflow runpullfrog.com𝕏

Comment on lines +17 to +18
if (commandLine[cliFlag] !== undefined) {
rspackEnabled = true;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When the CLI flag is present but set to false (e.g. --no-experimental-rspack), this unconditionally sets the module-level rspackEnabled to true, clobbering the config value. The returned value is correct, but the side effect is wrong — if getValue is called again without a commandLine flag, it would return true instead of the intended false.

Should this be rspackEnabled = commandLine[cliFlag] as boolean instead?

Suggested change
if (commandLine[cliFlag] !== undefined) {
rspackEnabled = true;
if (commandLine[cliFlag] !== undefined) {
rspackEnabled = commandLine[cliFlag] as boolean;

Comment on lines +22 to +23
"@rspack/core": "1.7.6",
"@rspack/plugin-react-refresh": "1.6.1",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: @rspack/core and @rspack/plugin-react-refresh are added as hard dependencies, which means every user of @remotion/bundler will download them even if they never enable the experimental flag. These are not small packages. Would optionalDependencies (with a dynamic require() guarded by a try/catch) or lazy require() at the call site be more appropriate for an experimental feature? The dynamic require('@rspack/core') in bundle.ts already exists, but rspack-config.ts has top-level static imports from @rspack/core that would fail if it weren't installed.

Comment on lines +1 to +3
import type {Configuration} from '@rspack/core';
import {DefinePlugin, ProgressPlugin, rspack} from '@rspack/core';
import ReactRefreshPlugin from '@rspack/plugin-react-refresh';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are top-level static imports, so @rspack/core and @rspack/plugin-react-refresh will be loaded as soon as this module is imported — even when the user hasn't enabled rspack. Since rspack-config.ts is re-exported from index.ts via BundlerInternals, this means all users pay the import cost. If rspack is moved to optionalDependencies, these would need to become dynamic imports. Even as hard deps, lazy-loading here would avoid the startup cost for webpack users.

Comment on lines +240 to +267
if (actualArgs.rspack) {
const {rspack: rspackFn} = require('@rspack/core');
const rspackCompiler = rspackFn(config);
const rspackOutput = await new Promise<{
toJson: (opts: unknown) => {
errors?: Array<{message: string; details: string}>;
};
}>((resolve, reject) => {
rspackCompiler.run(
(
err: Error | null,
stats: {
toJson: (opts: unknown) => {
errors?: Array<{message: string; details: string}>;
};
},
) => {
if (err) {
reject(err);
return;
}

rspackCompiler.close(() => {
resolve(stats);
});
},
);
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The inline types for the rspack compiler/stats duplicate the shape already available from @rspack/core. Since @rspack/core is a hard dependency, you could do:

const {rspack: rspackFn} = require('@rspack/core') as typeof import('@rspack/core');

This gives you full type safety from the rspack types without the manual toJson shape. Minor point for an experimental feature though.

Comment on lines +93 to +94
// Rspack config is structurally compatible with webpack config at runtime,
// but the TypeScript types differ. Cast through `any` for the override.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment is helpful. Worth noting that if a user's webpackOverride function inspects webpack-specific properties (e.g. checks config.module.rules for specific loader names), it could behave unexpectedly with an rspack config. Since this is experimental and behind a flag, probably fine for now — but the eventual docs for rspack usage should mention this.

Comment on lines +101 to +103
compiler = BundlerInternals.createRspackCompiler(
rspackConf,
) as unknown as webpack.Compiler;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Double-casting through unknown to webpack.Compiler is understandable for an experimental feature, but fragile. The wdm and webpackHotMiddleware calls downstream depend on the compiler having the right shape. If rspack's compiler diverges from webpack's in subtle ways, this could produce confusing runtime errors. Consider adding a brief comment about why this cast is safe (rspack's compiler implements the same interface).

Comment on lines +110 to +113
environment === 'development'
? [
new ReactRefreshPlugin({overlay: false}),
new rspack.HotModuleReplacementPlugin(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR title mentions "Fast Refresh not yet working." If HMR/React Refresh is known to be broken with this setup, should these plugins be omitted (or the inclusion gated with a TODO comment) to avoid confusing error output for users who try the experimental flag in development mode?

Comment on lines +86 to +94
experiments: {
lazyCompilation: isBun
? false
: environment === 'production'
? false
: {
entries: false,
},
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rspack supports experiments.lazyCompilation but the API shape may differ from webpack's (rspack docs show different options). Has this been verified to work with rspack, or does rspack silently ignore unknown experiment options?

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds experimental Rspack support as an alternative bundler to Webpack across the Remotion ecosystem. Rspack is a high-performance JavaScript bundler that aims to be compatible with Webpack's API while offering faster build times. The implementation introduces a new --experimental-rspack CLI flag and setExperimentalRspackEnabled() config option, though Fast Refresh functionality is not yet working (as noted in the PR title).

Changes:

  • Introduced Rspack configuration alongside Webpack with shared bundler logic extracted to common helper functions
  • Added rspack option throughout CLI commands (studio, render, still, bundle, benchmark, compositions) and API methods (Lambda/Cloud Run deploy)
  • Created new files for Rspack-specific configuration while refactoring Webpack config to share common patterns
  • Updated documentation across CLI and API docs to include the new experimental flag
  • Added test to ensure templates don't use the experimental feature by default

Reviewed changes

Copilot reviewed 36 out of 37 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
packages/renderer/src/options/rspack.tsx New option definition for experimental Rspack flag
packages/bundler/src/rspack-config.ts Main Rspack bundler configuration implementation
packages/bundler/src/shared-bundler-config.ts Extracted shared configuration logic between Webpack and Rspack
packages/bundler/src/define-plugin-definitions.ts Shared plugin definitions for both bundlers
packages/bundler/src/webpack-config.ts Refactored to use shared configuration helpers
packages/bundler/src/bundle.ts Updated to support both Webpack and Rspack compilation
packages/bundler/src/index.ts Exports Rspack-related functions for internal use
packages/bundler/package.json Added @rspack/core and @rspack/plugin-react-refresh dependencies
packages/studio-server/src/start-studio.ts Passes rspack flag to preview server
packages/studio-server/src/preview-server/start-server.ts Conditionally creates Rspack or Webpack compiler
packages/studio-server/src/preview-server/dev-middleware/setup-hooks.ts Updated to recognize Rspack compilation messages
packages/cli/src/*.ts Added rspack option to all CLI commands (studio, render, still, bundle, benchmark, compositions)
packages/cli/src/render-queue/*.ts Added rspack support to render queue processors
packages/cli/src/setup-cache.ts Passes rspack option through bundle flow
packages/cli/src/render-flows/*.ts Updated render flows to accept rspack parameter
packages/cli/src/config/index.ts Added setExperimentalRspackEnabled() config method
packages/lambda/src/api/deploy-site.ts Added rspack option to Lambda site deployment
packages/cloudrun/src/api/deploy-site.ts Added rspack option to Cloud Run site deployment
packages/docs/docs/*.mdx Documentation updates for CLI commands, config, and API methods
packages/it-tests/src/templates/validate-templates.test.ts Test to ensure templates don't enable experimental Rspack by default
bun.lock Lock file updates for new Rspack dependencies

Comment on lines 31 to 32
export {WebpackConfiguration, WebpackOverrideFn} from './webpack-config';
export {webpack};
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The RspackConfiguration type is not exported from the package, unlike WebpackConfiguration which is exported at line 31. This means users cannot properly type their Rspack-specific override functions or configurations.

Consider exporting RspackConfiguration alongside WebpackConfiguration to maintain consistency and provide proper type support for Rspack users.

Copilot uses AI. Check for mistakes.
cache: options.enableCaching
? {
type: 'filesystem',
name: getWebpackCacheName(options.environment, hash),
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cache naming strategy doesn't distinguish between Webpack and Rspack, which could lead to cache collision. When switching between Webpack and Rspack bundlers, they would try to reuse each other's caches, potentially causing issues since they generate different output formats.

Consider creating separate cache directories or prefixes for Webpack and Rspack. For example, you could modify getWebpackCacheName() to accept a bundler type parameter and include it in the cache name, or create a separate getRspackCacheName() function.

Suggested change
name: getWebpackCacheName(options.environment, hash),
name: `${process.env.REMOTION_BUNDLER ?? 'webpack'}-${getWebpackCacheName(options.environment, hash)}`,

Copilot uses AI. Check for mistakes.
Comment on lines +17 to +22
if (commandLine[cliFlag] !== undefined) {
rspackEnabled = true;
return {
value: commandLine[cliFlag] as boolean,
source: 'cli',
};
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The getValue function doesn't use the actual value from the command line. When commandLine[cliFlag] is defined, it should return that value, not just set rspackEnabled to true unconditionally.

The current implementation would always enable Rspack when the flag is present, even if it's explicitly set to false (e.g., --experimental-rspack=false). This is inconsistent with other options like experimentalClientSideRenderingOption which correctly uses the command line value at line 23.

Copilot uses AI. Check for mistakes.
Comment on lines +86 to +94
experiments: {
lazyCompilation: isBun
? false
: environment === 'production'
? false
: {
entries: false,
},
},
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The getBaseConfig() function includes a lazyCompilation configuration under the experiments key, which is a Webpack-specific feature that Rspack may not fully support. While Rspack's configuration structure is compatible with Webpack, this specific feature may not work as intended when using Rspack.

Consider documenting this limitation or conditionally excluding the lazyCompilation option when Rspack is being used. The PR title already mentions "Fast Refresh not yet working", and this could be related.

Suggested change
experiments: {
lazyCompilation: isBun
? false
: environment === 'production'
? false
: {
entries: false,
},
},

Copilot uses AI. Check for mistakes.
Comment on lines +10 to +12
description: () => (
<>Uses Rspack instead of Webpack as the bundler for the Studio or bundle.</>
),
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The description doesn't mention that this is an experimental feature or that Fast Refresh is not yet working. While the CLI flag name includes "experimental", the description itself should explicitly mention the current limitations to set proper expectations.

Consider updating the description to something like: "Experimental feature: Uses Rspack instead of Webpack as the bundler for the Studio or bundle. Note: Fast Refresh is not yet functional with Rspack."

Copilot uses AI. Check for mistakes.
if (useRspack) {
Log.warn(
{indent: false, logLevel},
'Enabling experimental Rspack bundler.',
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The warning message doesn't mention the current limitations of the Rspack integration, specifically that Fast Refresh is not yet working (as noted in the PR title). Users should be informed about these limitations when they enable the experimental feature.

Consider expanding the warning to mention known limitations, similar to how the client-side rendering warning provides a documentation link at line 114.

Suggested change
'Enabling experimental Rspack bundler.',
'Enabling experimental Rspack bundler. Fast Refresh is currently not supported and other limitations may apply. See https://www.remotion.dev/docs/rspack for details.',

Copilot uses AI. Check for mistakes.
const keyboardShortcutsEnabled = keyboardShortcutsOption.getValue({
commandLine: parsedCli,
}).value;
const rspack = rspackOption.getValue({commandLine: parsedCli}).value;
Copy link

Copilot AI Feb 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When the rspack flag is used with the bundle command, there's no warning message to inform users that they're using an experimental feature with known limitations (Fast Refresh not working). This is inconsistent with the studio command which does provide a warning at line 143-146 in studio.ts.

Consider adding a similar warning message for the bundle command to maintain consistency and inform users about the experimental nature and limitations of Rspack integration.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant