Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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:

  1. Links crate:rspack_binding_api
  2. 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.