react-native-runtimes Run heavy React Native UI and business logic in isolated Hermes runtimes — without freezing your main JS thread 📖 Docs · ⚡ Quick Start · 📦 Core · 🗂 State react-native-runtimes is built in collaboration with our friends at Callstack We love fast apps ⚡ and cars 🏎️ What is this? React Native gives your product one main JavaScript runtime. When…
react-native-runtimes is built in collaboration with our friends at Callstack
We love fast apps ⚡ and cars 🏎️
What is this?
React Native gives your product one main JavaScript runtime. When a feed, chat screen, editor, reducer, or hydration job monopolizes that runtime, input, animation, and navigation all start competing for the same VM.
react-native-runtimes adds a multi-runtime layer for React Native New Architecture apps:
mount selected React components in named secondary Hermes runtimes
run whole screens, headless tasks, and typed functions away from the main runtime
share state across isolated JS heaps through a native C++ singleton
prewarm runtimes before navigation so expensive surfaces are ready when users arrive
wire it through Metro and, for Expo, a config plugin instead of hand-written runtime plumbing
// That's it. This component now renders in its own Hermes instance.
<OnRuntime name="chat-runtime">
<MessageList conversationId={conversationId} />
</OnRuntime>
What it unlocks
If your app has…
You can move…
Main runtime keeps…
A chat, feed, or inbox that janks on mount
The expensive list or route surface
Navigation, gestures, and input
Reducers or stores competing with animation
Business logic in a long-lived runtime
Frame-critical UI work
Slow first-open screens
A prewarmed runtime before the user taps
Predictable navigation latency
Background hydration or decoding
Headless tasks on a worker runtime
Responsive startup and render
State that must be visible everywhere
Native-backed shared stores
Synchronous reads without bridge round-trips
When to reach for it
You have one or two expensive features that repeatedly monopolize the main JS runtime.
You want a chat, feed, editor, map sidebar, or media-heavy route to stay alive and warm.
You need business logic or cache hydration to run without blocking interaction.
You are already on React Native New Architecture with Hermes, or you are willing to move there.
When not to use it
Your app is simple enough that memoization, virtualization, or moving work off render fixes the issue.
You need legacy architecture or a non-Hermes JS engine.
You want to pass large mutable objects or non-serializable props directly between runtimes. Pass ids/keys and read shared data from native-backed state instead.
For navigation flows that should live entirely on a secondary runtime:
import { ThreadedScreen, threadedComponent } from '@react-native-runtimes/core';
export const ConversationScreen = threadedComponent<Props>(
'ConversationScreen',
(props) => <ConversationRoute {...props} />,
);
// In your navigator:
<ThreadedScreen
component={ConversationScreen}
props={{ conversationId }}
runtimeName={`chat-${conversationId}`}
/>
⚡ Runtime prewarming
Start the runtime before the user navigates so there is no cold-start lag:
import { ThreadedRuntime } from '@react-native-runtimes/core';
// e.g. when the inbox row becomes visible
await ThreadedRuntime.prewarm(`chat-${conversationId}`);
🏃 Headless tasks
Run JS on a named runtime without mounting a view — perfect for pre-hydrating stores, decoding data, or running reducers in a long-lived worker:
Call a typed function on a specific runtime and await the result — arguments and return values are JSON-serialized automatically:
import { call, runtimeFunction } from '@react-native-runtimes/core';
export const fibonacci = runtimeFunction((n: number) => ({
input: n,
result: fibonacciNumber(n),
computedAt: new Date().toISOString(),
}));
// Call it on a named runtime from the main runtime:
const result = await call(fibonacci).on('fibonacci-worker-runtime')(38);
Or use a function directive for fixed-runtime helpers — Metro rewrites the call site automatically:
async function refreshCache(key: string) {
'background'; // ← directive: this function always runs on 'background' runtime
await cacheStore.hydrate();
return cacheStore.get(key);
}
const value = await refreshCache('settings'); // cross-runtime, no extra API
🗂 Shared state — synchronous, cross-heap
A Zustand-style API backed by a native C++ process-wide singleton. Reads are synchronous. No bridge round-trip. Any runtime can write and every subscriber is notified.
import { createSharedStore } from '@react-native-runtimes/state';
export const chatStore = createSharedStore({
name: 'chat',
initialState: { messages: {}, settings: { theme: 'dark' } },
});
// Path handles for fine-grained subscriptions:
const roomMessages = chatStore.path<Message[]>(['messages', 'release-room']);
await roomMessages.update(items => [...(items ?? []), newMessage]);
// Subscribe with a selector — works in any runtime:
const count = roomMessages.use(items => items?.length ?? 0);
评论