Skip to main content
Version: 0.10 (unreleased)

Import forwarding

Import forwarding is a feature of Bundle Mode that lets you use third-party libraries inside worklets without adding boilerplate code.

To understand the motivation behind this feature, let's look at the ambiguity of using imports inside worklets.

Motivation

Say you have the following code:

import { foo } from 'my-library';

function worklet() {
'worklet';
foo();
}

scheduleOnUI(worklet);

Your intent is clearly to invoke foo from my-library inside the worklet — there shouldn't be any ambiguity. But consider another example.

Say my-library is a library that provides a counter:

my-library/index.js
let value = 0;

export function incrementValue() {
value++;
}

export function getValue() {
return value;
}

And then you use it like this:

import { incrementValue, getValue } from 'my-library';

incrementValue();

function worklet() {
'worklet';
const value = getValue();
console.log(value);
}

scheduleOnUI(worklet); // does it log 0 or 1?

Different JavaScript Runtimes don't share any state, so the invocation of incrementValue on the RN Runtime didn't affect the value variable on the Worklet Runtime.

So what's the intent behind the getValue invocation inside the worklet here? Did the author want to print the state of the RN Runtime or the Worklet Runtime? Both interpretations are valid, but during static analysis of the code — which the Worklets Babel plugin does — it's impossible to tell which one is correct.

Disambiguating manually

You can write the same code explicitly, without ambiguity.

Printing RN Runtime state

import { incrementValue, getValue } from 'my-library';

incrementValue();
const value = getValue();

function worklet() {
'worklet';
console.log(value);
}

scheduleOnUI(worklet); // logs 1

Here you explicitly assigned the result of getValue to a variable and passed that variable as a closure of the worklet. It's clear that you wanted to print the state of the RN Runtime.

Printing Worklet Runtime state

import { incrementValue } from 'my-library';

incrementValue();

function worklet() {
'worklet';
const { getValue } = require('my-library');
const value = getValue();
console.log(value);
}

scheduleOnUI(worklet); // logs 0

You used require to import getValue inside the worklet. There's no doubt that you wanted to print the state of the Worklet Runtime.

The cost of explicit disambiguation

Writing your code exactly as in the previous example requires you to re-specify all the imports in each worklet, which adds a lot of boilerplate:

function incrementOnce() {
'worklet';
const { getValue, incrementValue } = require('my-library');
incrementValue();
const value = getValue();
console.log(value);
}

function incrementTwice() {
'worklet';
const { getValue, incrementValue } = require('my-library');
incrementValue();
incrementValue();
const value = getValue();
console.log(value);
}

// etc...

Using import forwarding

Import forwarding lets you state which imports used inside worklets should be forwarded into the worklet body automatically, so you can drop that boilerplate.

You can configure it via the importForwarding option of the Worklets Babel plugin.

Dropping boilerplate

You can rewrite the code from the Printing Worklet Runtime state example without boilerplate. First, configure the Worklets Babel plugin:

/** @type {import('react-native-worklets/plugin').PluginOptions} */
const workletsPluginOptions = {
// ...
importForwarding: { moduleNames: ['my-library'] },
};

Then rewrite the code like this:

import { incrementValue, getValue } from 'my-library';

incrementValue();

function worklet() {
'worklet';
const value = getValue();
console.log(value);
}

scheduleOnUI(worklet); // logs 0

This works because you explicitly stated all imports from my-library should be forwarded into worklets — the worklet gets its own copy of my-library's state.

Explicit closure capture

What if you wanted to print the state of both the RN Runtime and the Worklet Runtime? The same closure pattern still works:

import { incrementValue, getValue } from 'my-library';

incrementValue();

const value = getValue();

function printRN() {
'worklet';
console.log(value);
}

function printWorklet() {
'worklet';
const value = getValue();
console.log(value);
}

scheduleOnUI(printRN); // logs 1
scheduleOnUI(printWorklet); // logs 0

To learn more about closures, see this section of the Worklets documentation.