Neon: writing Node.js native addons in Rust
Rust bindings for writing safe and fast native Node.js modules.
At a glance
- What is it?
- Neon is a Rust binding layer for building Node.js native addons, distributed as the neon crate plus the create-neon project generator. The design is sound and the toolchain is current, but the 1.0.0 migration and the experimental Bun support are the two things to check before you commit.
- Who is it for?
- Adopt Neon when you have a Rust crate whose hot path you want to call from Node without rewriting it in C, and when you can accept the 1.0.0 API break documented in doc/MIGRATION_GUIDE_1.0.0.md. Do not adopt it if you need guaranteed Bun compatibility, since the README states that some Node-API functions are not implemented in Bun, or if you are pinned below Rust 1.65.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 3 days ago.
- What is it written in?
- Mainly Rust, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 1, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The problem Neon solves for Rust and Node teams
Node.js is fast at I/O and slow at tight numeric loops. The usual fix is a native addon, and the usual way to write one is C or C++ against Node-API. That works, but the build setup is fiddly and the memory model is unforgiving. Neon exists to let you write that addon in Rust instead, keeping the safety guarantees Rust gives you while still producing a .node binary that require() can load.
The intended audience is narrow and specific. You already have Rust code, or you are willing to learn it, and you want it callable from JavaScript. Neon is not a general bridge for calling arbitrary JavaScript from Rust, and it is not a way to avoid writing glue code. Every exported function still needs a signature that matches what JavaScript will pass in, and every value crossing the boundary is converted explicitly through a context object.
The README describes the project as "Rust bindings for writing safe and fast Node.js native addons." That is the whole pitch. If your performance problem is in JavaScript, Neon does not help; if it is in a Rust crate you already own, it is the shortest path from that crate to an npm package.
How a Neon module is structured
A Neon addon is a Rust crate with a main function annotated with #[neon::main]. That function receives a ModuleContext and exports functions by name. Each exported function takes a FunctionContext and returns a JsResult, and inside it you build JavaScript values through the context rather than allocating them directly.
The README's example is the clearest illustration of the data flow. Values are created from the context, pushed into a JavaScript array, and the array is returned:
fn make_an_array(mut cx: FunctionContext) -> JsResult<JsArray> {
let n = cx.number(9000);
let s = cx.string("hello");
let b = cx.boolean(true);
let array = cx.empty_array();
array.set(&mut cx, 0, n)?;
array.set(&mut cx, 1, s)?;
array.set(&mut cx, 2, b)?;
Ok(array)
}The context is mutable and threaded through every call, which is what lets Neon track handles and hand them back to the JavaScript garbage collector correctly. The trade-off is verbosity: you cannot write a plain Rust function and export it, the signature has to be shaped for the boundary. The #[neon::main] attribute then registers the exports on the module:
#[neon::main]
fn main(mut cx: ModuleContext) -> NeonResult<()> {
cx.export_function("make_an_array", make_an_array)?;
Ok(())
}On the JavaScript side, the compiled module is loaded with require() like any other addon. The repository is laid out as both an npm workspace and a Cargo workspace, with crates/ holding the Rust crates and pkgs/ holding the JavaScript packages, which is worth knowing if you plan to read the source rather than the docs.
Installing Neon and running a first addon
The README points to a platform dependencies page before anything else, and that step is not optional: a Rust toolchain plus the native build tools for your OS are prerequisites. Once those are in place, project scaffolding is a single npm command:
npm init neon@latest my-projectThis generates a new addon project. The README then directs you to the Hello World guide for writing your first function, so the generated project is a starting point rather than a finished example. From there the workflow is the normal npm one: install dependencies, build, and load the resulting module from JavaScript.
If you are working inside the Neon repository itself rather than a generated project, the README gives the workspace test commands. The full suite runs through npm:
npm install
npm testIndividual pieces can be tested in isolation. A single JavaScript package uses an npm workspace command, and a single Rust crate uses a cargo workspace command:
npm --workspace=create-neon test
cargo test -p neon-buildThose two commands are the fastest way to confirm your environment is set up correctly before you start writing your own crate.
Where Neon is the wrong tool
The README is explicit that Bun support is experimental. It states that in many cases Neon modules will work in Bun, but that some Node-API functions are not implemented there, and links to the Bun issue tracker. If your deployment target is Bun, or you need one codebase that runs identically on Node and Bun, that gap is a real risk rather than a footnote.
Node version support has a similar shape. Neon supports current and maintenance releases of Node, and the README notes that older versions, down to a minimum of v10, may require lower Node-API versions. So if you are stuck on an old Node runtime for operational reasons, you are outside the tested path even though it is technically reachable.
On the Rust side, the floor is Rust stable 1.65. Projects pinned to an older toolchain cannot use current Neon. The README also states that testing happens on the latest stable, beta and nightly Rust, which tells you the floor is a floor and not a target.
The larger limitation is conceptual. Neon is for exporting Rust to JavaScript, not for embedding JavaScript in a Rust program, and not for calling into Node's internals. If your problem is the reverse direction, this is the wrong library, and no amount of configuration will change that.
Neon compared with napi-rs
The most common comparison for Neon is napi-rs, and the difference is in how much the binding layer does for you. Neon asks you to write the boundary yourself: you take a context, build values through it, and return a JsResult. The example above shows the pattern, and it is deliberate. You see every conversion.
napi-rs takes the opposite approach, generating bindings from annotated Rust so that more of the marshalling is handled for you. That means less boilerplate for straightforward functions, at the cost of a layer of code generation between your Rust and the emitted addon. With Neon, what you write is closer to what runs.
Neither approach is strictly better. If you are porting a large existing Rust API and want the export surface written by hand and reviewed line by line, Neon's explicitness is an advantage. If you want to expose dozens of functions with minimal ceremony, the generated approach will get you there faster. The choice is about how much of the boundary you want to own.
Maintenance, versions and the 1.0.0 break
The repository is not archived, and the last push was on 2026-09-11. The most recent release listed is 1.2.0-alpha.0 from 2025-12-05, with 1.1.1 and 1.1.0 both landing on 2025-05-23. The stable line is therefore 1.1.x, and 1.2.0 is still a pre-release at the time of writing.
The upgrade cost is dominated by one event. The README states that 1.0.0 included several breaking changes made to fix unsoundness, improve consistency and add features, and it points to doc/MIGRATION_GUIDE_1.0.0.md. If you have an addon written against a pre-1.0 Neon, that guide is required reading before you touch anything, and the word "unsoundness" in the release description is the reason the break was taken rather than avoided.
Licensing is dual, and the README states it plainly: Apache License 2.0 or the MIT license, at your option. Both files are present in the repository root as LICENSE-APACHE and LICENSE-MIT. That is a permissive arrangement typical of Rust ecosystem projects, but the choice between the two is yours to make and worth confirming with whoever handles licensing on your side.
Editorial conclusion
Adopt Neon when you have a Rust crate whose hot path you want to call from Node without rewriting it in C, and when you can accept the 1.0.0 API break documented in doc/MIGRATION_GUIDE_1.0.0.md. Do not adopt it if you need guaranteed Bun compatibility, since the README states that some Node-API functions are not implemented in Bun, or if you are pinned below Rust 1.65. Before writing code, verify two things: that your Node version is a current or maintenance release, and whether your existing addon predates 1.0.0, because the migration guide is the first document you will need.
Frequently asked questions
What does Neon actually mean in the context of Node.js and Rust?
Neon is a set of Rust bindings for writing native Node.js addons, described in the README as bindings for writing safe and fast native addons. You write Rust functions shaped for the JavaScript boundary and export them from a module.
How do you use Neon to start a new addon project?
After installing the platform dependencies, the README gives the scaffolding command npm init neon@latest my-project, and then directs you to the Hello World guide for writing your first function.
How does Neon work at the code level?
A module has a function annotated with #[neon::main] that receives a ModuleContext and exports functions. Each exported function takes a FunctionContext, builds JavaScript values through it, and returns a JsResult.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/neon-bindings-neon)