Rationale of Rspack Custom Binding
Rspack is so fast because it's written in Rust, along with Rspack's internal built-in plugins and built-in loaders.
Most of the time, we assume you've been using the Rspack JavaScript API and writing Rspack JavaScript Plugins.
You might have heard there are some overheads when using the JavaScript API. The rumor is true!
Since Rspack is mostly written in Rust, providing the adaptation layer with the JavaScript API requires significant overhead. This overhead comes from:
- Passing values back and forth between Rust and JavaScript
- Type conversions between languages
- Memory allocation and deallocation
These factors create performance bottlenecks.
But have you ever wondered if there's a way to extend Rspack's functionality by writing native Rust code? A way that:
- Doesn't sacrifice performance
- Provides access to rich Rust APIs
- Avoids JavaScript-Rust overhead
The answer is yes. This is where Rspack Custom Binding comes in.
To get started with Rspack Custom Binding, you need to know the surface level of how Rspack binding works.
How Rspack Binding Works
If you are using @rspack/cli
or @rspack/core
without knowing what a custom binding is, you are using Rspack binding.
It's a simple architecture that allows you to extend Rspack's functionality by leveraging the Rspack JavaScript API. This works the same way you use the Webpack JavaScript API to extend Webpack.
Let's take a deep dive into the architecture. It contains 3 parts:
npm:@rspack/core
: The JavaScript API layer of Rspack. Written in JavaScript.npm:@rspack/binding
: The Node.js Addon of Rspack.crate:rspack_binding_api
: The N-API glue layer of Rspack. Written in Rust.
flowchart TD Core("npm:@rspack/core") style Core stroke-width:0px,color:#FFDE59,fill:#545454 Core --> Binding("npm:@rspack/binding") style Binding stroke-width:0px,color:#FFDE59,fill:#545454 Binding --> APIs("crate:rspack_binding_api") style APIs stroke-width:0px,color:#FFDE59,fill:#545454
crate:rspack_binding_api
The N-API glue layer of Rspack.
This layer contains glue code that bridges the gap between N-API-compatible runtimes (typically Node.js) and Rust Core crates.
npm:@rspack/binding
The Node.js Addon of Rspack.
This layer performs two key functions:
- Links
crate:rspack_binding_api
- Compiles it into a Node.js Addon (a
*.node
file) with NAPI-RS
The functionalities that npm:@rspack/core
provides are mostly exposed by the Node.js Addon in npm:@rspack/binding
.
Note: If you've checked the code on npm, you'll notice it doesn't contain the *.node
file.
This is because the *.node
files are distributed by platform-specific packages:
@rspack/binding-darwin-arm64
(for macOS ARM)@rspack/binding-linux-x64
(for Linux x64)- And others for different platforms
Don't worry about this for now—we'll cover the details in the custom binding section.
npm:@rspack/core
The JavaScript API layer of Rspack.
The internals of npm:@rspack/core
are written in JavaScript. It bridges the gap between the Node.js Addon in npm:@rspack/binding
and the Rspack JavaScript API.
npm:@rspack/cli
is a command line tool that uses npm:@rspack/core
to build your project.
How Rspack Custom Binding Works
Let's use the diagram below to understand how a custom binding works. The diagram shows two states:
- "Before": The standard Rspack setup
- "After": The custom binding approach
Before State
Your project uses the default Rspack binding. This is created solely from crate:rspack_binding_api
, the core glue layer between Rust and Node.js.
After State
You introduce your own native code. As the diagram shows, your User Customizations (like custom Rust plugins and loaders) are combined with the original crate:rspack_binding_api
.
This combination produces a new, personalized Custom Binding with several benefits:
- Becomes your project's new Node.js addon
- Allows injecting high-performance, custom logic directly into Rspack's build process
- Eliminates JavaScript-Rust communication overhead for your custom code
Key Advantage
You can continue to use npm:@rspack/core
with your custom binding. This approach provides:
- Native performance and customization: Your code runs at Rust speed
- No API rewriting needed: Keep using the existing JavaScript API layer
- Feature reuse: Access all features that
@rspack/core
provides
We will cover how to integrate @rspack/core
with a custom binding in a later section.
flowchart LR subgraph Before ["_Before_"] Original("crate:rspack_binding_api") style Original stroke-width:0px,color:#FFDE59,fill:#545454 end subgraph After ["_After_"] Plugin("User Customizations:<br>- custom plugins<br>- custom loaders") style Plugin stroke-width:0px,color:#AB7F45,fill:#FFE2B1 API("crate:rspack_binding_api") style API stroke-width:0px,color:#FFDE59,fill:#545454 Plugin --> CustomBinding("Custom Binding = <br>crate:rspack_binding_api + User Customizations") API --> CustomBinding style CustomBinding stroke-width:0px,color:#AB7F45,fill:#FFE2B1 end Before -.-> After style Before stroke-dasharray: 5 5 style After stroke-dasharray: 5 5
Next Steps
Now you have a basic understanding of how Rspack Custom Binding works. Let's move on to the Create From Template guide to set up your development environment.