JSI (JavaScript Interface) — is een softwarelaag in React Native die directe synchrone toegang biedt vanuit JavaScript naar C++ objecten en functies, ter vervanging van de asynchrone JSON-brug Bridge. In tegenstelling tot zijn voorganger maakt JSI het mogelijk om native methoden aan te roepen zonder serialisatie van berichten en om verwijzingen naar C++ objecten rechtstreeks naar de JS-omgeving door te geven. Volgens gegevens van React Native Team (2025) zorgt JSI voor een tot 10-voudige versnelling van de interactie tussen JS en native code in scenario's met intensieve gegevensuitwisseling.
Belangrijkste
JSI (JavaScript Interface) — is een C++ laag die de JavaScript-engine (Hermes, JavaScriptCore, V8) de mogelijkheid biedt om rechtstreeks toegang te krijgen tot C++ objecten, functies en geheugen. In tegenstelling tot Bridge, die aanroepen serialiseerde naar JSON en ze via een asynchrone wachtrij verstuurde, stelt JSI JS-code in staat om synchroon C++ methoden aan te roepen en het resultaat onmiddellijk te ontvangen.
JSI werd geïntroduceerd in React Native 0.64 als onderdeel van de nieuwe architectuur (New Architecture). Het hoofddoel was het elimineren van de bottleneck die Bridge vormde: elke interactie tussen JS en native code kostte tijd aan serialisatie, deserialisatie en overdracht via de berichtenwachtrij. JSI lost dit probleem op door de JS-engine directe toegang tot C++ objecten te bieden via wrappers die de interface jsi::Value, jsi::Object en jsi::Function implementeren.
JSI is geen „eén-op-één” vervanging van Bridge — het is een fundamenteel andere benadering van integratie. Bridge werkte als een postbus: JS stuurde een bericht, het ging door de wachtrij, de native kant verwerkte het en stuurde een antwoord. JSI werkt als een pointer: JS ontvangt een verwijzing naar een C++ object en kan de methoden ervan synchroon aanroepen, net als gewone JS-functies. Dit is het fundamentele verschil in de architectuur van de interactie tussen twee omgevingen.
De noodzaak voor JSI ontstond door de beperkingen van de originele Bridge, die in 2015 in React Native was ingebouwd. Naarmate de populariteit van het framework groeide en applicaties complexer werden, werd het prestatieprobleem duidelijk: elke aanroep van een native module had minstens 3–5 ms nodig voor serialisatie. Voor eenvoudige bewerkingen, zoals het lezen van een sensor waarde of het verkrijgen van de schermgrootte, was dit acceptabel, maar voor animaties, grafisch werk en streaming gegevensverwerking — kritisch. Het React Native team begon in 2019 met het werk aan de nieuwe architectuur, en JSI werd de fundering ervan.
JSI is ontworpen als een abstractie over JavaScript-engines heen. Het biedt een uniforme C++ API die wordt geïmplementeerd voor elke specifieke engine: Hermes, JavaScriptCore (iOS), V8 (Android). Dit betekent dat de ontwikkelaar zich geen zorgen hoeft te maken over verschillen tussen engines — Fabric en TurboModules werken hetzelfde ongeacht welke JS-engine wordt gebruikt.
Aan de basis van JSI ligt het concept van Host Objects — C++ objecten die naar de JS-omgeving worden geëxporteerd als native JS-objecten. Wanneer JS-code toegang krijgt tot een eigenschap of methode van zo'n object, onderschept JSI de aanroep en delegeert deze naar de overeenkomstige C++ methode. Dit gebeurt synchroon, in dezelfde thread, zonder contextwisseling en zonder geheugentoewijzing voor een JSON-string.
Elk Host Object implementeert de interface jsi::HostObject met de methoden get, set en getPropertyNames. De JS-engine roept deze methoden aan bij elke toegang tot de eigenschappen van het object. Bijvoorbeeld, bij de aanroep van NativeModule.someMethod() in JS, transformeert JSI deze aanroep naar een C++ aanroep van de overeenkomstige methode van het Host Object. De geretourneerde waarde wordt teruggegeven aan JS als jsi::Value — een gegeneraliseerd type dat een getal, string, booleaanse waarde, object of undefined kan vertegenwoordigen.
Een belangrijk kenmerk van JSI is de afwezigheid van een berichtenwachtrij. Bridge gebruikte een asynchrone wachtrij: JS stuurde een verzoek, schakelde over naar andere taken, de native kant verwerkte het verzoek en het resultaat kwam terug via een callback. JSI werkt synchroon: als JS een methode van een native module aanroept via JSI, wordt de uitvoering van JS-code gepauzeerd totdat het resultaat is verkregen. Dit vereenvoudigt de logica (u hoeft niet te wachten op callbacks) en elimineert race conditions, maar vereist voorzichtigheid — lange synchrone aanroepen blokkeren de JS-thread.
Waarden die via JSI zijn gecreëerd, leven in de runtime-omgeving van de JS-engine en worden beheerd door de garbage collector. Wanneer C++ code een jsi::String of jsi::Object maakt en teruggeeft aan JS, beheert de omgeving automatisch het geheugen. Als C++ code een verwijzing naar een JS-waarde tussen aanroepen wil behouden, gebruikt het jsi::Value::getWeak() of de globale jsi::Object::setProperty met behoud van de verwijzing op het root-object van de runtime. Dit voorkomt voortijdige verwijdering door de garbage collector.
JSI is standaard niet thread-veilig. Alle aanroepen van JSI-methoden moeten afkomstig zijn uit de thread waarin JS wordt uitgevoerd (meestal is dit de JS-thread van React Native). Als een native module achtergrondwerk in een aparte thread start, moet het resultaat worden teruggegeven via de JS-thread met behulp van de runOnJS-aanroep uit TurboModules. Deze beperking is de prijs voor synchroniteit en de afwezigheid van serialisatie.
Het verschil tussen JSI en Bridge is van fundamentele aard en beïnvloedt alle aspecten van de interactie tussen JS en native code. Bridge was asynchroon, serialiseerde gegevens naar JSON en gebruikte een berichtenwachtrij; JSI is synchroon, werkt met native verwijzingen en vereist geen serialisatie.
| Parameter | Bridge | JSI |
|---|---|---|
| Aanroepmodel | Asynchrone wachtrij | Synchrone directe aanroep |
| Serialisatie | JSON (serialisatie + deserialisatie) | Geen (directe verwijzingen naar C++ objecten) |
| Vertraging | 3–10 ms per aanroep | 0.1–0.5 ms per aanroep |
| Typering | Dynamisch (via JSON) | Statisch (via Codegen) |
| C++ integratie | Alleen via native modules (Java/ObjC) | Direct, zonder tussenpersonen |
| Thread | Aparte native thread | JS-thread (synchroon) |
Volgens gegevens van het React Native Team heeft de migratie van Bridge naar JSI in de Facebook Marketplace-app de opstarttijd met 35% verkort en het geheugengebruik met 20% verminderd door het elimineren van gegevensduplicatie tussen JS en de native kant.
Bridge was geen „fout” — het was een architectonische beslissing die gerechtvaardigd was op het moment van de creatie van React Native in 2015. Native ontwikkeling voor twee platforms met verschillende talen vereiste een universeel uitwisselingsformaat. JSON als serialisatieformaat was beschikbaar op alle platforms en maakte het mogelijk om de interactie te uniformeren. Het probleem werd later duidelijk, toen React Native werd gebruikt voor complexe applicaties met duizenden native module-aanroepen per seconde.
React Native behoudt achterwaartse compatibiliteit: native modules die voor Bridge zijn geschreven, blijven werken in de nieuwe architectuur via een compatibiliteitslaag. Voor nieuwe modules wordt echter aanbevolen om direct JSI via TurboModules te gebruiken. Migratie van bestaande modules bestaat uit het vervangen van het interactieprotocol zonder de bedrijfslogica van de module zelf te wijzigen.
JSI is de fundamentele laag waarop alle componenten van de nieuwe React Native architectuur zijn gebouwd. Zonder JSI zouden noch Fabric (de nieuwe renderer) noch TurboModules (geoptimaliseerde native modules) kunnen werken. JSI biedt een uniforme manier van interactie tussen JS en C++ op alle niveaus.
Fabric — de nieuwe React Native renderer die JSI gebruikt voor synchrone toegang tot C++ UI-weergaven. In de oude architectuur verliep rendering via Bridge: JS maakte React-elementen, serialiseerde ze naar JSON, stuurde ze via Bridge, de native kant deserialiseerde en creëerde de UI. Fabric creëert via JSI C++ Shadow Tree objecten rechtstreeks vanuit JS, berekent synchroon de Layout via Yoga en geeft de kant-en-klare frames door aan de native renderer — zonder enige serialisatie.
TurboModules — is de evolutie van native React Native modules. In plaats van de module in Bridge te registreren en zijn methoden via JSON aan te roepen, gebruikt TurboModules JSI voor lazy loading en directe aanroep. Wanneer JS-code voor het eerst toegang krijgt tot een module, creëert JSI een Host Object — het laadt de native module en biedt zijn methoden aan als C++ functies. Lazy loading betekent dat de module geen geheugen verbruikt tot het eerste gebruik — dit is vooral belangrijk in applicaties met tientallen native modules, waarvan er vele alleen op bepaalde schermen worden gebruikt.
Voor het werken met JSI in de nieuwe architectuur wordt Codegen gebruikt — een tool die C++ wrappers genereert uit JavaScript-specificaties. De ontwikkelaar beschrijft de interface van de native module in TypeScript of Flow, en Codegen genereert C++ code die een JSI-compatibele Host Object implementeert. Dit automatiseert het routinematige werk en garandeert dat de types aan de JS- en C++-zijde gesynchroniseerd zijn.
Laten we bekijken hoe het werken met JSI er in de praktijk uitziet. In dit voorbeeld wordt een eenvoudige C++ klasse gemaakt die via JSI naar JS wordt geëxporteerd, en de methode ervan wordt aangeroepen vanuit JavaScript-code van React Native.
// Calculator.h — C++ klasseheader toegankelijk vanuit JS
class Calculator {
public:
double add(double a, double b) { return a + b; }
double multiply(double a, double b) { return a * b; }
};
De klasse Calculator bevat twee rekenkundige methoden. We moeten deze toegankelijk maken vanuit JS. Hiervoor maken we een Host Object dat Calculator inwikkelt en zijn methoden via JSI aanbiedt.
// CalculatorHostObject.cpp — JSI wrapper implementatie
class CalculatorHostObject : public jsi::HostObject {
private:
Calculator calc;
public:
jsi::Value get(jsi::Runtime& runtime,
const jsi::PropNameID& name) override {
auto propName = name.utf8(runtime);
if (propName == "add") {
return jsi::Function::createFromHostFunction(
runtime, name, 2,
[this](jsi::Runtime& runtime,
const jsi::Value& thisVal,
const jsi::Value* args,
size_t count) -> jsi::Value {
return jsi::Value(calc.add(
args[0].asNumber(),
args[1].asNumber()));
});
}
return jsi::Value::undefined();
}
};
In deze code wordt de methode get elke keer aangeroepen wanneer JS toegang krijgt tot een eigenschap van het object. Als de naam van de eigenschap „adden” is, wordt een C++ functie geretourneerd die twee argumenten uit JS ontvangt en calc.add() aanroept. De waarde wordt geretourneerd als jsi::Value — JSI converteert automatisch double naar een JS-getal.
Na registratie van het Host Object in de JS-omgeving ziet de aanroep eruit als een gewone JS-functie. Alle types worden gecontroleerd in de codegeneratiefase, wat type-mismatch fouten tijdens de uitvoering elimineert.
// JavaScript — C++ calculator aanroepen via JSI
import { Calculator } from 'react-native-calculator'
const result = Calculator.add(5, 3)
console.log(result) // 8 — synchroon, geen vertraging
const product = Calculator.multiply(4, 2.5)
console.log(product) // 10 — onmiddellijk resultaat
Let op: het resultaat wordt onmiddellijk geretourneerd, zonder Promise, zonder await, zonder callbacks. Dit is een synchrone aanroep, die onmogelijk was in de Bridge-architectuur. Voor langdurige bewerkingen (bestand lezen, netwerkverzoek) moeten asynchrone patronen worden gebruikt — JSI elimineert de noodzaak voor achtergrondthreads voor zware taken niet.
In de praktijk schrijven de meeste ontwikkelaars geen JSI Host Objects handmatig — dit werk wordt gedaan door Codegen, die C++ wrappers genereert op basis van TypeScript-specificaties. Het begrijpen van hoe JSI onder de motorkap werkt, is echter noodzakelijk voor het effectief debuggen van prestaties en bij het maken van complexe native modules die directe toegang tot C++ bibliotheken (Skia, FFmpeg, OpenCV) vereisen.
Veelgestelde vragen
Bridge werkt asynchroon via JSON-serialisatie en een berichtenwachtrij — elke aanroep heeft 3–10 ms nodig voor gegevensconversie. JSI biedt synchrone directe toegang tot C++ objecten zonder serialisatie, waardoor de vertraging wordt teruggebracht tot 0.1–0.5 ms. JSI ondersteunt ook het doorgeven van verwijzingen naar objecten, niet hun kopieën.
Ja, JSI biedt een uniforme C++ API die is geïmplementeerd voor Hermes (standaard in React Native), JavaScriptCore (iOS) en V8 (Android). De ontwikkelaar hoeft geen verschillende code te schrijven voor verschillende engines — Fabric en TurboModules werken hetzelfde op alle ondersteunde engines.
Ja, React Native biedt een laag voor achterwaartse compatibiliteit. Native modules die voor Bridge zijn geschreven, blijven werken in de nieuwe architectuur. Het wordt echter aanbevolen om ze naar TurboModules te migreren om te profiteren van de voordelen van JSI — lazy loading en synchrone aanroepen.
Voor dagelijks gebruik — nee. De TypeScript-specificaties van native modules worden automatisch gecompileerd naar C++ wrappers via Codegen. Kennis van C++ is alleen nodig bij het maken van eigen C++ bibliotheken of bij het debuggen van de prestaties van JSI op runtime-niveau.
JSI lost drie belangrijke problemen van Bridge op: hoge vertraging door JSON-serialisatie, gebrek aan synchrone aanroepen en het onvermogen om complexe objecten via verwijzing door te geven. JSI maakt ook directe integratie van C++ bibliotheken mogelijk, zonder de Java of Objective-C laag.
Samenvatting
We ontwikkelen een mobiele applicatie turnkey
IT Sectr creëert sinds 2017 iOS- en Android-applicaties voor startups en bedrijven. We adviseren u en stellen de beste oplossing voor.
Lees ook