Back to all episodes

RNR 353 - Node-API Support for React Native with Kræn Hansen

February 6, 2026
32:28
E
353
Kræn Hansen, Robin Heinze, Mazen Chami

Mazen and Robin sit down with Kræn Hansen from ElevenLabs to break down what Node API actually is and why it could be a game-changer for React Native library authors who want to write native modules once and use them everywhere, plus what still needs to happen before it's ready for prime time.

 

Show Notes


Connect With Us!


This episode is brought to you by Infinite Red!

Infinite Red is an expert React Native consultancy located in the USA. With over a decade of React Native experience and deep roots in the React Native community (hosts of Chain React and the React Native Newsletter, core React Native contributors, creators of Ignite and Reactotron, and much, much more), Infinite Red is the best choice for helping you build and deploy your next React Native app.

Jed Bartausky:

Hello everyone, and welcome back to another episode of the React Native Radio Podcast. Episode 353, Node API Support for React Native with Kræn Hansen.

 

Mazen Chami:

Welcome back, React Native Radio listeners. I'm Mazen, she's Robin, and we're React Native Radio. Today we have a fun topic for you, one that Rob and I kind of enjoyed prepping for, a topic that is deep in the weeds of React Native, so it'll be a fun one.

 

Robin Heinze:

It was new to me. I had to do some research.

 

Mazen Chami:

Yeah, same. And I'm glad it came with a talk and a blog post.

 

Robin Heinze:

Yes, really, really appreciated that I could watch a talk to get some background.

 

Mazen Chami:

Exactly. We have with us Kræn Hansen. Kræn Hansen, welcome to the show. It's nice to have you here.

 

Kræn Hansen:

Thank you so much. Thank you for having me.

 

Mazen Chami:

Yeah. Why don't you go ahead and introduce yourself to our listeners. Let us know how you got to where you're at, what do you do?

 

Kræn Hansen:

So my background is in databases. I helped build Realm, the mobile database, local first database and sync service. And I did that for about seven years. And five and a half years of that was at MongoDB since we got acquired. And Realm got deprecated and I got transferred to the developer tools team. And this is like less APIs and more GUIs and terminal UIs. But I've been spending a lot of my time with React Native and with these databases, local first stuff. And now since actually January 5th, I joined Leaverlabs, building use cases for them on their developer experience team. So I can also talk a little about that, but I'm still learning right now. And speaking about learning, I learned React Native on the job at Realm. It's many years ago now. And I've been slowly just building my knowledge around building native code for React Native, Node.js, the browser.

Yeah.

 

Mazen Chami:

Nice. Yeah. Yeah. Realm is one. Offline mode is one that I think we tend to always forget of as mobile developers, unless it's a requirement on the app. You never know when ... Well, in today's world, does data actually drop off? Not necessarily, but always good to prepare yourself for that.

 

Robin Heinze:

We're also chronically online. We forget that it's possible to be offline.

 

Kræn Hansen:

It's true. It's true. We tend to forget, especially when we are just using simulators as we are building the apps. I think offline first is one part of now what has resurrected as the local first movement. I think something that's less obvious maybe is the developer experience when you use a local first approach. So when you have to cross a network barrier, you need to do some async work like waiting for a network. And in that case, you need to have awaiting of promises and you need to maybe do an optimistic update of your UI and you need to handle rolling that back.

You need to show errors when stuff didn't work. And it's like disjoint from the point of interaction when you started the thing and then when you have to show the thing or retry the thing. But when you have all your data locally, you can afford to have synchronously blocking the function calls when you do your changes to your database. And that's a massive increase in developer experience because you get the symptom of not being able to write to a database because you hit some invariant, like you created something with the same ID as something you already had in your database, but you get that in the function, basically in the callback for your click handler. And that's a massive gain in developer experience. There's so much code that you don't have to write when you're doing it that way.

 

Mazen Chami:

Yeah, that's awesome. Well, spoiler alert to our listeners. That is not our topic for today, but I think we need to have you back to talk about this.

 

Robin Heinze:

Hey, it really sets the stage for how smart Kræn Hansen is and how much backend experience and stuff he has, which is going to be really important for our actual topic, which is Node API, which we'll get to.

 

Mazen Chami:

Before we do that, let's hear from our sponsor. Infinite Red is the premier React Native consultancy located fully remote in the US. We're a team of 30 senior plus level React Native developers and support staff and have been doing this for over a decade.

 

Robin Heinze:

Ah, you fixed the copy. Good job.

 

Mazen Chami:

If you're looking for React Native expertise for your next project, hit us up at infinite.red/radio. Don't forget to mention that you heard about us through the React Native Radio podcast. So let's get into our topic for today. Our topic is actually Node API support for React Native. Kræn Hansen, the objective of this episode, since it's very technical and kind of low level, deep in the weeds, is make Node API, explain it like I'm five to us. And why is it relevant as a React Native developer? We might be asking, I'll speak for myself. I may be asking some dumb questions. I like to say there are no dumb questions, but this is a topic that I feel like I don't really have the depth in because I don't necessarily know how Node will come into React Native. I've used it on the web, but not necessarily in that.

So go ahead. Why did React Native need another native module path? Give us a kickstart on what is Node API in React Native?

 

Kræn Hansen:

Yeah, I think just let's start taking even a step further back. It's like one of the big benefits of writing apps using React Native for many app developers is that they don't have to care about the native stuff. They can write JavaScript and that's all they need to know in many cases. And I think that also goes for module systems like these native code module systems. Most application developers don't necessarily need to interact with Node API directly. And while I'm ... So in some sense, you could just drop off and not listen to the episode. I hope you don't do that. But because there's some nuances to that statement and also just having knowledge of what underlying technology a library is built upon also builds up more ideas and knowledge around how it can be used and the potential velocity you can expect from the library developer, stuff like that, what kind of interoperability you can expect.

So as I'm speaking about this, I do want you to keep me honest if I'm going too far in the weeds and I need to be drawn back to the topic and ... Yeah.

 

Mazen Chami:

Well, as with most of our episodes, let's start high level origin, motivation, and then kind of dig deeper into native stuff as we go on.

 

Kræn Hansen:

Let me talk about first up, what is Node API not? Okay. Perfect. So we have stuff in Node.js that we know as the runtime APIs. These are the Node file system APIs or Node Path or the process global, stuff like that. That's not what I'm talking about. So it's not how to access file systems or how to do path manipulation.

 

Robin Heinze:

Which is probably mostly what our listeners have, what they think of is you get FS from Node and

 

Kræn Hansen:

Like— I'm talking about something very specific called Node API. I'm not talking about the Node.js API, if that makes sense. It's a very small distinction, but it's a huge difference. Okay?

 

Robin Heinze:

Naming is hard. Naming is hard.

 

Kræn Hansen:

So it used to be called NAPI and because in some circles, it's considered a derogative term. So they renamed from NAPI to Node API. So it had a name that was more different in the past. Okay. Yeah. Okay. So also just prefacing, if something, some of the stuff that I'm talking about here, maybe we can also leave this later in the conversation. But as you mentioned, I did do a talk on this at React Universe and we also have multiple blog articles on the CallStack blog.

 

Robin Heinze:

Which we'll link all of it in the show notes. You should definitely, definitely watch the talk. Both Mazen and I watched it, and it's really helpful to see the code examples and stuff that are in there.

 

Kræn Hansen:

If we talk about the motivation, I think I started talking about Realm and why I got there. And I do think that it links back to my experience with Node API. So Realm had this C++ core library that we wanted to call into from Node.js and we wanted to call into it from React Native. And it was really hard to maintain that implementation, to be honest. We had a C++ implementation against first JavaScriptCore, which was the runtime and engine that was used at that time in React Native. And then when Hermes came along, we did an implementation against this thing called JSI, the JavaScript interface, which is the preferred way of building C++ native modules nowadays using Turbo modules calling into JSI. And we also at the same time had an implementation against Node API because we also published our library as a Node.js compatible binary.

So we had to maintain at some point three. And now because JavaScriptCore also has, there's an implementation of JSI for JavaScriptCore, we could get rid of one of them, so we had two,

But it would've been so awesome if we could have just had one implementation and then we could have used that across all the different engines that we needed to support. And it's just really hard to maintain these implementations, especially when you have a large API surface. Trying to write JavaScript in C++ is really hard. You have to do a lot of stuff that you don't need to do when you're just writing JavaScript. So I learned two things from that. Basically, I wanted to write native code using more modern and more opinionated tools for testing, docs, packages, stuff like that. And C++ might be a good tool for this in some trenches of the ecosystem, but it also carries a huge legacy. I don't know how to put it, but that's just one thing. I think for me, the next native library that I built, I want to use Rust for that in particular for me.

Node API is not Rust only at all. It's a C++ based, C based API, but because Node API is defined as a C API, so it has a header file in C. Because of that, it's also callable from Rust in a nice way. And that's just one thing that leads me to my path onto Node API is that I saw this thing where I could use Node API from Rust and next time I wanted to build something native, I wanted to use Rust.

 

Robin Heinze:

So that was kind of the initial entry point into this. You're like, "I really want to be able to build native modules for React Native using Rust rather than using C++ or Kotlin or Swift." In terms of JSI, which I think that's going to be key for the listeners to really understand what the purpose of this is, because I think most of them probably understand JSI at this point. We've talked about the new architecture a lot. Is Node API meant to replace Turbo modules in JSI for most libraries? Is it meant to complement? Is it meant to work with? What's the relationship with JSI in terms conceptually? Is it a replacement or is it sort of a complement?

 

Kræn Hansen:

I would see Node API as a lower level abstraction that can replace JSI and Turbo modules altogether. I don't think that you shouldn't see it as something that replaces JSI and Turbo modules in your app entirely, but I think from a library developer's point of view, you can use Node API instead of JSI and Turbo modules.

 

Robin Heinze:

Right. Okay. So yeah, it sort of serves the same purpose as JSI and Turbo modules. And as a library author, I could say, I really want to write my library with something other than C++, Kotlin, and Swift, maybe Rust. So I'm going to choose Node API rather than choosing—

 

Kræn Hansen:

I would say the caveat that it is ... I did this wrong because it is not a Rust only API, right? It's a—

 

Robin Heinze:

Right,

 

Kræn Hansen:

Right.

 

Robin Heinze:

Actually, it sounds like that's one of the—

 

Kræn Hansen:

That's just my motivation.

 

Robin Heinze:

Right. That's your motivation, but it actually is quite multi-platform. It can be used for a lot of different platforms besides Rust, but yes.

 

Kræn Hansen:

Anything that has a C-based foreign function interface invocation system can basically, that could be Zig, that could be Rust, that could be C++, Swift. All kinds of different, like Java, Kotlin, can all call into C APIs, and that's why you're able to use Node API from those. And some of those languages have better developer experience than others. And Rust in particular has a really good one, I think.

 

Robin Heinze:

I'm going to let Mazen sneak in here.

 

Mazen Chami:

Yeah. I had a follow-up question to what you said. So if I'm listening to this and I'm a library maintainer, this is mainly for me. If I'm an app developer, it pertains to me if I'm going down to the native side to build a native module. I just built a native module recently. Now, here's a question. There are some native modules in today's world. Let's put Node API a little bit off to the side for a second. If I'm looking to build a native module, my decision comes on, can I build it in C++? Is it available in C++? Some aren't, right? For example, Maps, so there's the Apple MapKit or Google Places API. Map API, those need to be built in Swift or Kotlin due to their nature. They might be UI-based, view-based, they might be module-based. How can I, as someone who wants to use Node API and/or one of these C languages leverage something like this?

Or is this a limitation just like as is C++ today?

 

Kræn Hansen:

I think you're spot on. I think it's a drawback that I haven't personally spent too much time building. If you remember, I used to build a database, and that's the angle of attack that I have in my mind. So I think there are opportunities, I believe, to orchestrate views over Node API, but it's not something I've done. And it's not something that I've seen done much of in the community with the exception of NativeScript and JMA Birch, where you basically, they have a binding from Node, sorry, from React Native into their NativeScript runtime over Node API. So they're very interested in doing that, but it's a little different. It's basically where you expose all the platform APIs through a JavaScript interface and they want to use code generation and Node API for that. But if you're just a regular library author doing views with Node API, something that's just not done, it hasn't been done.

So yeah, I think that's my most honest answer.

 

Mazen Chami:

No, no, that totally makes sense. I mean, it's also a limitation of C++ right now, and that's something the community is working hard on trying to leverage and figure out because it would be nice to, in the future, only learn one native language, a performant native language so that you can build views, modules easily, quickly, and also get the performance that we need in React Native, rather than having to learn JavaScript, TypeScript, Swift, and Kotlin. And now it's just JavaScript, TypeScript, C++, or in this scenario, Node API specifically.

 

Kræn Hansen:

And I think two things that I wanted to say is that both Turbo modules and also Nitro modules are much more mature in a React Native context, but Node API is much more mature outside of React Native. So there's binding generators and tooling available for Node API outside of the context of React Native that we are also leaning into where that's not the case for Turbo and Nitro modules. So that's like those two disjoint ecosystems potentially coming more together.

 

Robin Heinze:

A lot of the excitement I've seen around this initiative to get Node API React Native compatible has been from people who came from other worlds and are now doing React Native and are wanting the functionality that they have used in other places to be available in React Native and they're very excited about that.

 

Kræn Hansen:

Yeah. We've seen attention from the tangential communities like the Lynx people from ByteDance and also the BegaOS by Amazon. Both of those are exploring using Node API as their module system. So there's also a potential for interoperability between modules. And I think as a library author, that's something that I want. I really don't want to have to test my thing across all these different permutations of module systems. And I just want to write it once and I want to get it out to as many people as possible. And I think Node API is potentially a future that can help me do that.

 

Robin Heinze:

In the blog and in the talk, I keep hearing the phrase like ABI stable, and that's AB, not AP. Just to make things a little bit more confusing, Node API is ABI stable. Briefly, what does ABI stable mean in practice for React Native developers and library authors? Why is that something that we would want?

 

Kræn Hansen:

So maybe you can see the difference as like the API is the module, like the method names and the function signatures that you know from JavaScript, like you have this function, like you have an object that has some fields and then it has some methods and then you can call those. Those are the signatures that it has, like the types or whatever. That's the API of the thing. And in C++, you have to be a little more diligent about, or you have to be a little more detailed about it because layout as you're saving this object into memory, the order in which fields are presented in your structure, in your class actually matters on the level of memory. And when I have two parts of my process trying to access the same object with a given type signature, if one part of the system thinks that name came before age and the other one thinks that age came before name, you have this problem where one is writing to the part of the memory in one layout and the other one is reading in a different layout.

And this layout is what we call the application binary interface because that's the binary representation in memory of the structure of the class.

 

Robin Heinze:

So what the machine is reading?

 

Kræn Hansen:

Yeah. How the machine translates raw memory into an understanding of the object.

 

Robin Heinze:

Right? So I mean, that's why traditionally all of our native modules that we're bringing in, we have to build, we have to compile them locally after we install them every time.

 

Kræn Hansen:

Yeah. There have been some issues where if you have a prebuilt binary, like something that calls into other native code, and I built that on my machine and I expect React Native to use a memory layout in a certain way. And that's captured when I build my stuff. When then React Native goes along and updates by, for example, putting in a new field in the top of that structure, everything goes wrong because I'm calling into it assuming that it has a layout that it no longer has, and then the application crashes.

 

Robin Heinze:

Right. Yay. Which is why updating is always traditionally painful.

 

Kræn Hansen:

So I think there's a tradition, it's actually a big problem I think for the React Native ecosystem that we haven't really solved, but we are approaching a better solution. Meta has this idea that, and they also have discussions where they basically bluntly said, "We don't prebuild at Meta." We build everything from source at Meta. So we always get the latest source code and we just use that. And then you have folks at Microsoft that do the complete opposite thing where they want to do prebuilt DLLs, like dynamic libraries that they can build once and then reuse forever without having to rebuild them again.

 

Robin Heinze:

Interesting.

 

Kræn Hansen:

And that clash basically, it also is also what actually led Microsoft to build Node API support into React Native Windows was basically have an interface between their native modules and their React Native applications that was ABI stable on the boundary between those.

 

Mazen Chami:

Got it. Okay. Well, I kind of want to pivot a little bit to the developer workflow, developer experience type of perspective. Can you walk us through probably as easily as possible, a minimal Hello World path a library author would have to go through to get Hello World to output on the screen, whether it's a module that returns Hello World or just a view that displays Hello World on the native side.

 

Kræn Hansen:

Frankly, right now I'm searching on the CallStack incubator GitHub. We have a good, that's like an example.

 

Robin Heinze:

I'm—

 

Kræn Hansen:

Going to pull that up. It's the Node API example lib. It's implemented in C++. It's a very simple addon. It's just addon: when you load it, it returns an object that has a method called Sum, and then you can call it with two numbers and it will sum them up basically. And what do you do as a library author? You have to declare a dev dependency on something called cmake-rn. It's a tool that we help maintain to basically drive a CMake project. And CMake is a meta build system. It's a system that you can use to generate projects, native code projects like Xcode projects or makefiles to build native code,

C++ native code. And we use cmake-rn, this tool that we built to basically make CMake Node API aware, so it knows how to link against, how to present Node API to your C++ code. There's another project called CMake.js, so not RN, but JS, and that was used in the community to do the same thing, exact same thing, but for Node.js specifically. So it's a comparable tool, but for the React Native ecosystem. Okay. So you use that tool and you invoke it just with a CMakeLists.txt file in your root. And that file basically just declares like these are the C++ source files that I'm pointing to, and it has the version of Node API that you want to basically build with. And we can get into that later, but the Node API has a version basically that you can increase when you want to adopt new features, but that also puts a constraint on what environments you can run in because some of those older environments that could be like an old version of Node.js wouldn't necessarily support that version of Node API.

 

Mazen Chami:

Awesome. Yeah. We will make sure to include in the show notes a link to the GitHub repo that Kræn Hansen was talking about. It's the GitHub CallStack—

 

Robin Heinze:

And the CallStack incubator docs too.

 

Mazen Chami:

Yes, exactly. We'll include all those, but I think like Kræn Hansen mentioned that React Native Node API GitHub repo is very, very important and helpful to get to visualize this. Because again, something like this, show me how I'm going to do Hello World is more visual than it is, let's talk about it. But you did a good job of explaining it.

 

Robin Heinze:

I want to talk a little bit about what application developers can kind of expect the impact of seeing libraries built with Node API to be. It sounds like build times is probably one if these libraries are coming precompiled. I mean, similar to what the React Native core team did recently with React Native itself where it's coming in prebuilt and builds are much, much faster. If our native modules are also prebuilt, it seems like build time is going to be one big impact that we may see for these specific libraries. Are there other advantages or impacts we could see as application developers installing Node API built libraries?

 

Kræn Hansen:

And that's a great point. The build time, I didn't mention that because of the API stability that you hinted to, that means that you can prebuild these binaries. And for some functions, that doesn't matter too much. It could be either or. It does matter in some ways, like if I'm using some weird compiler setting or something like that, that might not be available on your C++ compiler. So there's something about being able to basically get your ... As a library author, you get your errors as you're building your library. It used to be the case for Realm that we got this a lot. When Xcode got updated, people started posting issues on our issue tracker because stuff stopped building, because some things changed in the way that the compiler read our project, basically. So instead of getting your issues, and basically when you're using a library that uses Node API, the promise is that the library author can get their compiler errors upfront, and they don't have to rely on you posting issues when stuff breaks, specifically for compiler issues, which is a very ... It's super hard to reproduce those things.

It's like, "Can you tell me what compiler version you used? And are you using some kind of compiler cache? Are you using any strange flags or..." Yeah, it's just a mess.

 

Mazen Chami:

Yeah. Well, we're getting close to 30 minutes, so I kind of want to keep that in mind and shift a little bit to talk about future of Node API in React Native and what has to happen and what's next for Node API to basically be, let's say, prebuilt by default in React Native so that it's available and it becomes real and everyone can use it.

 

Kræn Hansen:

I should have said this from the top. So the support for Node API in React Native has a prerequisite on Hermes, the engine supporting Node API. And there's a pull request that was opened by Vladimir from Microsoft in April, not last year, but the year before. And it has been building momentum and he's been building out tests. He's doing a great job. There's a lot of work that has gone into this, but we're still not at a point where it's completely ready to merge. There were some issues in Hermes having been built with Microsoft tooling for some time and that has been unblocked. So now it's ready to be worked on again. So that's one thing that needs to happen is for the Hermes request adding Node API to merge. It's to be determined when that happens. But I also understand that it's a very hard ask to ask app developers to use a fork of Hermes.

It is pretty opaque from the app developer's point of view. I've done a lot of stuff to try to make it less daunting to build with a fork of Hermes. It's fully automated. You just have to run one command on Android and then on iOS, it's driven by the CocoaPods project. But yeah, that's one thing. And then what other things need to happen? So there's some of the implementation of Node API that needs to happen in React Native in our, what we call the host package, and that also has some outstanding tasks. And the main thing blocking that is basically having great tests to do this. We want to make sure that we implement it the same way that Node.js does it. And for that to happen reliably, I'm working on building something called the Node API compatibility test suite or compliance test suite, CTS it's called.

And I'm building that in the Node.js repository actually. I'm trying to help them build this up where you can have this test suite that you can run across all the implementers of Node API, Bun, Deno, Node.js itself, and also React Native. So we can have one big test corpus that we can run across all these implementations to just really make sure that we understand that we have a very good implementation basically. Yeah.

 

Mazen Chami:

Awesome. Yeah. Thank you for that, Kræn Hansen. That was very helpful. And I think—

 

Robin Heinze:

Yeah, this is super interesting and not something I expected to be super interesting.

 

Mazen Chami:

Yeah. Yeah. It's a very niche topic, right?

 

Robin Heinze:

It is, but it's really cool to see. It kind of feels like bringing React Native up to the level of a lot of other areas of programming.

 

Kræn Hansen:

I think when you look around, there's a lot of stuff happening in the JavaScript ecosystem around rustifying different implementations, like taking—

 

Robin Heinze:

Bundlers,

 

Kræn Hansen:

Taking formatters, stuff like that, and moving them into Rust. And something that really powers that is the binding generators, Node API binding generators for specifically Rust. I just want us to say some of this seems very new and very risky, but we are standing on some very broad shoulders of a lot of people in the Node.js community that has done tons of work to make this reliable and stable. Yeah.

 

Mazen Chami:

Awesome. Well, Kræn Hansen, thank you so much for coming on the episode. This was very insightful and giving us another perspective on—

 

Robin Heinze:

I feel like I understand what Node API is and how it works and why you would use it.

 

Mazen Chami:

Totally. And I urge our listeners, please check out the show notes. We've put a lot of resources in there and I'm sure check out the repo, help out however you can, if this is something of interest to you and something that can help your project. Before we sign off, Robin, do you have a mom joke for us?

 

Robin Heinze:

Oh, I have a good one. Why was E the only letter to get presents from Santa? Because all the other letters were naughty.

 

Mazen Chami:

All right, listeners, I'll leave you with that for the end of the episode.

 

Robin Heinze:

See you all next time. All right. Thanks everyone. Bye-bye.

 

Jed Bartausky:

As always, thanks to our editor, Todd Werth, our assistant editors, Jed Bartausky and Tyler Williams, our marketing and episode release coordinator, Justin Huskey, and our guest coordinator, Mazen Chami. Our producers and hosts are Jamon Holmgren, Robin Heinze, and Mazen Chami. Thanks to our sponsor, Infinite Red. Check us out at infinite.red/radio. A special thanks to all of you listening today. Make sure to subscribe to React Native Radio on all the major podcasting platforms.

 

Photo of Gant Laborde and Mark Rickert hugging at a retreat.Photo of Todd Werth laughing during an online team game. Other members of the team are in the background.Photo of team members Jed Bartausky and Carlin Isaacson at a team dinner.Photo of Darin Wilson sitting at a table listening to a presentation

Ready to get started with us? Chat with our team over zoom

There’s no perfect time to get started. Whether you have a formal proposal or a few napkin sketches, we’re always happy to chat about your project at any stage of the process.

Schedule a call