<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>react-native-workers blog</title>
        <link>https://ammarahm-ed.github.io/react-native-workers/blog</link>
        <description>Release announcements and deep dives on react-native-workers.</description>
        <lastBuildDate>Sun, 26 Jul 2026 00:00:00 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <copyright>Copyright © 2026 react-native-workers.</copyright>
        <item>
            <title><![CDATA[react-native-workers 1.0.0-alpha: real multithreading for React Native]]></title>
            <link>https://ammarahm-ed.github.io/react-native-workers/blog/introducing-react-native-workers</link>
            <guid>https://ammarahm-ed.github.io/react-native-workers/blog/introducing-react-native-workers</guid>
            <pubDate>Sun, 26 Jul 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[React Native has always been single-threaded where it matters most. Your JavaScript]]></description>
            <content:encoded><![CDATA[<p>React Native has always been single-threaded where it matters most. Your JavaScript
runs on one thread, and anything heavy you do there — parsing a big payload, hashing,
compression, image work, a chatty reducer, a busy render — competes with the UI for
the same runtime. The usual advice is "move it native," but that means writing a
native module for every heavy thing you'd rather just write in JS.</p>
<p><strong>react-native-workers</strong> brings the Web's answer to this problem to React Native:
real background threads, each with its own JavaScript runtime, behind the <code>Worker</code>
API you already know from the browser. Today it enters <strong>public alpha</strong> as
<code>1.0.0-alpha</code>.</p>
<p>This post is the full tour — what it is, how it's built, everything it can do
(including some things a worker library usually can't), how hard it's been tested,
what's still rough, and where it's going.</p>
<div class="showcase_AzwL"><figure class="figure_hngk" style="width:262px"><div class="bezel_ktx_"><div class="screen_XbMa"><video class="media_V7ZR" src="/react-native-workers/img/video/imagefx.mp4" aria-label="The example app applying invert, sepia and pixelate filters to an image; each run reports the filter time and the worst UI frame gap" autoplay="" loop="" muted="" playsinline="" preload="metadata"></video></div></div><figcaption class="caption_Fis_">Image filters running in a worker on pixels held in a <code>SharedBuffer</code>. Each run reports the worst UI frame gap it caused — one frame, while a 250&nbsp;ms blur runs.</figcaption></figure><figure class="figure_hngk" style="width:262px"><div class="bezel_ktx_"><div class="screen_XbMa"><img class="media_V7ZR" src="https://ammarahm-ed.github.io/react-native-workers/img/screens/parse.webp" alt="Parallel parse screen: 120,000 log lines parsed by 1, 2, 4 and 8 workers, taking 54ms, 29ms, 18ms and 17ms — a 3.18× speedup" loading="lazy"></div></div><figcaption class="caption_Fis_">The same 2.58&nbsp;MB of logs parsed by 1, 2, 4 and 8 nested workers over one shared buffer — 54&nbsp;ms down to 17&nbsp;ms.</figcaption></figure></div>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-shape-of-it">The shape of it<a href="https://ammarahm-ed.github.io/react-native-workers/blog/introducing-react-native-workers#the-shape-of-it" class="hash-link" aria-label="Direct link to The shape of it" title="Direct link to The shape of it" translate="no">​</a></h2>
<p>If you've used a Web Worker, there's nothing new to learn:</p>
<div class="language-js codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-js codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports"> </span><span class="token imports maybe-class-name">Worker</span><span class="token imports"> </span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'@ammarahmed/react-native-workers'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> worker </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">Worker</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'./workers/heavy'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">worker</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method-variable function-variable method function property-access" style="color:#d73a49">onmessage</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">e</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token console class-name">console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">log</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'result:'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> e</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">data</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">worker</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">postMessage</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">items</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">100_000</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// returns instantly; runs off the UI thread</span><br></div></code></pre></div></div>
<div class="language-js codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-js codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// workers/heavy.js — a normal module that runs on its own thread</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method-variable function-variable method function property-access" style="color:#d73a49">onmessage</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">e</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> total </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">expensiveWork</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">e</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">data</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">items</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// never blocks the UI</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">postMessage</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">total</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><br></div></code></pre></div></div>
<p>That's the shape; for a real one, the <a class="" href="https://ammarahm-ed.github.io/react-native-workers/docs/tutorials/parallel-parse">parallel-parsing tutorial</a>
moves JSON parsing off the UI thread step by step. A worker takes a couple of options
that are worth knowing up front:</p>
<div class="language-js codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-js codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> worker </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">Worker</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'./workers/importer'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">nativeModules</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// let the worker reach the app's native modules (more below)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">name</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'importer'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// shows up in logs and in the DevTools target list</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></div></code></pre></div></div>
<p>Call <code>worker.terminate()</code> when you're done and the whole runtime + thread goes away.
Everything else — <code>postMessage</code>, <code>onmessage</code>, <code>onerror</code>, nested workers — behaves the
way you'd expect coming from the browser.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="inline-workers-when-a-file-is-overkill">Inline workers, when a file is overkill<a href="https://ammarahm-ed.github.io/react-native-workers/blog/introducing-react-native-workers#inline-workers-when-a-file-is-overkill" class="hash-link" aria-label="Direct link to Inline workers, when a file is overkill" title="Direct link to Inline workers, when a file is overkill" translate="no">​</a></h3>
<p>For a one-off or a dynamically generated worker, skip the file and pass source
directly. Inline workers need no Babel plugin and no bundling step — which also makes
them the path that works everywhere today (more on that in <a href="https://ammarahm-ed.github.io/react-native-workers/blog/introducing-react-native-workers#what-alpha-means" class="">What "alpha" means</a>):</p>
<div class="language-js codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-js codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> doubler </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">Worker</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">inline</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token template-string string" style="color:#e3116c">self.onmessage = (e) =&gt; self.postMessage(e.data * 2);</span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="when-something-throws">When something throws<a href="https://ammarahm-ed.github.io/react-native-workers/blog/introducing-react-native-workers#when-something-throws" class="hash-link" aria-label="Direct link to When something throws" title="Direct link to When something throws" translate="no">​</a></h3>
<p>Errors in a worker don't crash your app — they surface on <code>onerror</code>, with the source
location carried across the thread boundary:</p>
<div class="language-js codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-js codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">worker</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method-variable function-variable method function property-access" style="color:#d73a49">onerror</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">e</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token console class-name">console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">log</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">e</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">message</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'at'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> e</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">filename</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">':'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> e</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">lineno</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="its-not-built-on-worklets--each-worker-is-a-full-runtime">It's not built on Worklets — each worker is a full runtime<a href="https://ammarahm-ed.github.io/react-native-workers/blog/introducing-react-native-workers#its-not-built-on-worklets--each-worker-is-a-full-runtime" class="hash-link" aria-label="Direct link to It's not built on Worklets — each worker is a full runtime" title="Direct link to It's not built on Worklets — each worker is a full runtime" translate="no">​</a></h2>
<p>A common first question: <em>is this Reanimated/Worklets under the hood?</em> It isn't, and
the difference is the whole point.</p>
<p>react-native-workers spins up a <strong>separate Hermes runtime on its own OS thread</strong> for
each worker, with its own event loop, its own bundle, and full <code>import</code> support.
That's the Web Worker model: real, independent runtimes that talk over messages.</p>
<p>Worklets are a different tool — they run serialized closures on a <em>shared</em> runtime
with shared values, which is perfect for driving animations but isn't a place to run a
whole module graph. react-native-workers runs <strong>alongside</strong> worklets; it's for the "I
have real work to do off the main thread" case.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="under-the-hood">Under the hood<a href="https://ammarahm-ed.github.io/react-native-workers/blog/introducing-react-native-workers#under-the-hood" class="hash-link" aria-label="Direct link to Under the hood" title="Direct link to Under the hood" translate="no">​</a></h2>
<p>It helps to know what actually happens when you write <code>new Worker('./workers/heavy')</code>:</p>
<ul>
<li class=""><strong>A fresh Hermes runtime is created on a dedicated thread</strong> (via
<code>hermes::makeHermesRuntime</code>). Nothing is shared implicitly — the worker has its own
globals, its own module registry, its own garbage collector, capped at Hermes'
256 MB heap default.</li>
<li class=""><strong>Worker files are compiled into their own bundles.</strong> A Babel plugin picks up each
<code>new Worker('./path')</code>, points it at a bundle built from that module (and its
imports), and Metro serves or ships that bundle. You write normal modules; you
don't hand-manage bundles. Inline workers skip this entirely.</li>
<li class=""><strong>Messages cross via a structured-clone codec</strong> — objects, arrays, <code>Date</code>, typed
arrays, <code>ArrayBuffer</code>, and cycles are serialized between runtimes.</li>
<li class=""><strong>A per-worker <code>CallInvoker</code> keeps everything thread-consistent.</strong> Async native work
and promises resolve back on <em>the worker's own thread</em>, not the main one — which is
what makes the native features below safe rather than a source of cross-thread
crashes.</li>
</ul>
<p>The worker's global scope is set up to feel familiar: <code>self</code>, <code>postMessage</code>,
<code>structuredClone</code>, <code>queueMicrotask</code>, a <code>Worker</code> constructor for nesting,
<code>NativeEventEmitter</code>, and the native-module accessors described next.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="reaching-the-native-side">Reaching the native side<a href="https://ammarahm-ed.github.io/react-native-workers/blog/introducing-react-native-workers#reaching-the-native-side" class="hash-link" aria-label="Direct link to Reaching the native side" title="Direct link to Reaching the native side" translate="no">​</a></h2>
<p>A background thread that can only do pure computation is useful. A background thread
that can reach the <strong>native side</strong> is a different class of tool — and this is where
react-native-workers goes past a typical worker library.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="two-tiers-c-by-default-platform-modules-on-request">Two tiers: C++ by default, platform modules on request<a href="https://ammarahm-ed.github.io/react-native-workers/blog/introducing-react-native-workers#two-tiers-c-by-default-platform-modules-on-request" class="hash-link" aria-label="Direct link to Two tiers: C++ by default, platform modules on request" title="Direct link to Two tiers: C++ by default, platform modules on request" translate="no">​</a></h3>
<p>There are two levels of native access, by design:</p>
<ul>
<li class=""><strong>C++/JSI modules are available in every worker</strong> out of the box — including this
library's own module, which is what lets a worker create <em>nested</em> workers.</li>
<li class=""><strong>The app's platform (Java/Objective-C) TurboModules and legacy native modules are
opt-in</strong>, because they're heavier and not all of them are safe off the main thread.
Turn them on per worker with <code>nativeModules: true</code>.</li>
</ul>
<div class="language-js codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-js codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> worker </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">Worker</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'./workers/db'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token literal-property property" style="color:#36acaa">nativeModules</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></div></code></pre></div></div>
<p>Worker-unsafe modules — anything that assumes the UI thread, the surface registry, and
so on — are <strong>denylisted</strong> so they can't be pulled into a worker by accident. On iOS
there's no setup; on Android you hand the library your package list once. The
<a class="" href="https://ammarahm-ed.github.io/react-native-workers/docs/guides/native-modules">native modules guide</a> covers the model, and the
<a class="" href="https://ammarahm-ed.github.io/react-native-workers/docs/tutorials/transfer-manager">download-manager tutorial</a> puts native modules to
work inside a worker end to end.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="expo-modules-inside-a-worker--on-ios-and-android">Expo Modules, inside a worker — on iOS <em>and</em> Android<a href="https://ammarahm-ed.github.io/react-native-workers/blog/introducing-react-native-workers#expo-modules-inside-a-worker--on-ios-and-android" class="hash-link" aria-label="Direct link to expo-modules-inside-a-worker--on-ios-and-android" title="Direct link to expo-modules-inside-a-worker--on-ios-and-android" translate="no">​</a></h3>
<p>This is the one I'm most excited about. In an Expo app, <code>requireNativeModule(...)</code>
works <strong>directly</strong> inside a worker, across the whole module surface:</p>
<div class="language-js codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-js codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// inside a worker (nativeModules: true, in an Expo app)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token maybe-class-name">Device</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">requireNativeModule</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'ExpoDevice'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token maybe-class-name">Device</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">osName</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain">                         </span><span class="token comment" style="color:#999988;font-style:italic">// constants — read synchronously</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> </span><span class="token maybe-class-name">Device</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">getDeviceTypeAsync</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain">     </span><span class="token comment" style="color:#999988;font-style:italic">// async functions — invoked natively</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token maybe-class-name">Crypto</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">requireNativeModule</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'ExpoCrypto'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token maybe-class-name">Crypto</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">randomUUID</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain">                   </span><span class="token comment" style="color:#999988;font-style:italic">// sync functions — a value, not a Promise</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token maybe-class-name">Foo</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">requireNativeModule</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'SomeModule'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token maybe-class-name">Foo</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">someProperty</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain">                      </span><span class="token comment" style="color:#999988;font-style:italic">// live properties, read on access</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> sub </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token maybe-class-name">Foo</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">addListener</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'onX'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> handle</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// module events, delivered into the worker</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">sub</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">remove</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></div></code></pre></div></div>
<p>Constants, sync <strong>and</strong> async functions, live properties, and module events — the
whole thing, running through Expo's real implementation, with nothing crossing between
runtimes (that would crash). The two platforms get there differently, and getting both
working was most of the effort behind this release:</p>
<ul>
<li class="">On <strong>iOS</strong>, the Swift↔C++ boundary makes a second Expo <code>AppContext</code> impractical, so
the library installs its own <code>global.expo.modules</code> inside the worker that <strong>forwards
each call natively</strong> through Expo's public <code>AppContext</code> API — native values in,
native values out, results delivered back on the worker thread.</li>
<li class="">On <strong>Android</strong>, Expo's JNI install accepts a raw runtime pointer, so the library
builds a genuine <strong>per-worker Expo <code>AppContext</code></strong> and lets Expo install <code>global.expo</code>
against the worker runtime directly — every feature runs through Expo's own code.</li>
</ul>
<p>From your side, it's just <code>requireNativeModule</code>. Because these lean on Expo internals,
the version matrix (below) exists specifically to catch the day Expo changes them. The
<a class="" href="https://ammarahm-ed.github.io/react-native-workers/docs/installation#expo-modules-inside-a-worker">installation guide</a> has the Expo setup,
and the repo's <a href="https://github.com/ammarahm-ed/react-native-workers/tree/main/expo-example" target="_blank" rel="noopener noreferrer" class="">Expo example</a>
runs a worker probe that exercises the whole surface on both platforms.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="uiworker--drive-native-ui-from-a-worker">UIWorker — drive native UI from a worker<a href="https://ammarahm-ed.github.io/react-native-workers/blog/introducing-react-native-workers#uiworker--drive-native-ui-from-a-worker" class="hash-link" aria-label="Direct link to UIWorker — drive native UI from a worker" title="Direct link to UIWorker — drive native UI from a worker" translate="no">​</a></h3>
<p>Beyond request/response, <strong><code>UIWorker</code></strong> lets worker code render and update native
components, with events delivered natively rather than round-tripping through the main
JS thread. UIWorker runtimes are <strong>shared and persistent</strong> by default — asking for the
same worker twice reconnects to the already-loaded runtime instead of re-evaluating it
— with an <code>independent</code> option when you want a private, 1:1-lifetime runtime like a
background <code>Worker</code>. See the <a class="" href="https://ammarahm-ed.github.io/react-native-workers/docs/guides/ui-worker">UIWorker guide</a> for the full API,
and the <a class="" href="https://ammarahm-ed.github.io/react-native-workers/docs/tutorials/uiworker-demo">UIWorker demo</a> and
<a class="" href="https://ammarahm-ed.github.io/react-native-workers/docs/tutorials/worker-native-components">native components from a worker</a> tutorials for
it in action.</p>
<div class="showcase_AzwL"><figure class="figure_hngk" style="width:228px"><div class="bezel_ktx_"><div class="screen_XbMa"><video class="media_V7ZR" src="/react-native-workers/img/video/uiworker-animate.mp4" aria-label="A square view rotating at 60fps driven from a UIWorker, still animating while the app's JS thread is blocked for 2000ms" autoplay="" loop="" muted="" playsinline="" preload="metadata"></video></div></div><figcaption class="caption_Fis_">A UIWorker's own 60&nbsp;fps loop writing a view's transform directly. It keeps running while the app's JS thread is blocked for two seconds.</figcaption></figure><figure class="figure_hngk" style="width:228px"><div class="bezel_ktx_"><div class="screen_XbMa"><img class="media_V7ZR" src="https://ammarahm-ed.github.io/react-native-workers/img/screens/uiworker.webp" alt="UIWorker demo screen: 10,000 direct Obj-C calls at 0.1µs per call with onMain=true, and the same calls from a background Worker refused with an error" loading="lazy"></div></div><figcaption class="caption_Fis_">10,000 direct UI calls at 0.1&nbsp;µs each from the main thread — and the same calls refused when they come from a background worker.</figcaption></figure><figure class="figure_hngk" style="width:228px"><div class="bezel_ktx_"><div class="screen_XbMa"><img class="media_V7ZR" src="https://ammarahm-ed.github.io/react-native-workers/img/screens/nativecomponent.webp" alt="A live MKMapView with custom pins rendered by a view manager that was written in JavaScript and registered from a UIWorker" loading="lazy"></div></div><figcaption class="caption_Fis_">A real <code>MKMapView</code> host component whose view manager is JavaScript, registered from inside a UIWorker — no native code, no codegen.</figcaption></figure></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="sharing-state-without-copying-everything">Sharing state without copying everything<a href="https://ammarahm-ed.github.io/react-native-workers/blog/introducing-react-native-workers#sharing-state-without-copying-everything" class="hash-link" aria-label="Direct link to Sharing state without copying everything" title="Direct link to Sharing state without copying everything" translate="no">​</a></h2>
<p>Messages are great until you're moving a lot of data back and forth. So there are
shared primitives that live across runtimes — <a class="" href="https://ammarahm-ed.github.io/react-native-workers/docs/shared-data/shared-store"><code>SharedStore</code></a>,
<a class="" href="https://ammarahm-ed.github.io/react-native-workers/docs/shared-data/shared-value"><code>SharedValue</code></a>, and
<a class="" href="https://ammarahm-ed.github.io/react-native-workers/docs/shared-data/shared-buffer"><code>SharedBuffer</code></a> (there's an
<a class="" href="https://ammarahm-ed.github.io/react-native-workers/docs/shared-data/overview">overview</a> tying them together).</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="sharedstore--a-cross-runtime-keyvalue-store">SharedStore — a cross-runtime key/value store<a href="https://ammarahm-ed.github.io/react-native-workers/blog/introducing-react-native-workers#sharedstore--a-cross-runtime-keyvalue-store" class="hash-link" aria-label="Direct link to SharedStore — a cross-runtime key/value store" title="Direct link to SharedStore — a cross-runtime key/value store" translate="no">​</a></h3>
<div class="language-js codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-js codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports"> </span><span class="token imports maybe-class-name">SharedStore</span><span class="token imports"> </span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'@ammarahmed/react-native-workers'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> store </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">SharedStore</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'prefs'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">store</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">set</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'theme'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'dark'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> unsub </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> store</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">subscribe</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'theme'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">v</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">applyTheme</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">v</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// from any other runtime:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">store</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">get</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'theme'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 'dark'</span><br></div></code></pre></div></div>
<p><code>set</code> / <code>get</code> / <code>has</code> / <code>delete</code> / <code>keys</code> / <code>subscribe</code> — a small, observable store any
runtime can read, write, and watch.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="sharedvalue--one-shared-observable-value">SharedValue — one shared, observable value<a href="https://ammarahm-ed.github.io/react-native-workers/blog/introducing-react-native-workers#sharedvalue--one-shared-observable-value" class="hash-link" aria-label="Direct link to SharedValue — one shared, observable value" title="Direct link to SharedValue — one shared, observable value" translate="no">​</a></h3>
<p>Handy for a progress figure or a flag that the UI wants to reflect live:</p>
<div class="language-js codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-js codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports"> </span><span class="token imports maybe-class-name">SharedValue</span><span class="token imports"> </span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'@ammarahmed/react-native-workers'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> progress </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">SharedValue</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'import-progress'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// in the worker:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">progress</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">value</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.5</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// on the UI:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">progress</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">subscribe</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">v</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">setProgress</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">v</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></div></code></pre></div></div>
<figure class="figure_hngk" style="width:280px"><div class="bezel_ktx_"><div class="screen_XbMa"><video class="media_V7ZR" src="/react-native-workers/img/video/downloads.mp4" aria-label="Six transfer progress bars filling smoothly while a counter shows the worker made over a thousand shared writes against a few hundred rendered frames" autoplay="" loop="" muted="" playsinline="" preload="metadata"></video></div></div><figcaption class="caption_Fis_">Six transfers in one worker, each writing progress into a shared cell hundreds of times a second. The UI samples the cells once per frame — the write count runs far ahead of the frame count, which is the point.</figcaption></figure>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="sharedbuffer--raw-shared-memory">SharedBuffer — raw shared memory<a href="https://ammarahm-ed.github.io/react-native-workers/blog/introducing-react-native-workers#sharedbuffer--raw-shared-memory" class="hash-link" aria-label="Direct link to SharedBuffer — raw shared memory" title="Direct link to SharedBuffer — raw shared memory" translate="no">​</a></h3>
<p>The zero-copy building block: a block of memory shared across runtimes, with a lock so
writers and readers don't tear:</p>
<div class="language-js codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-js codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports"> </span><span class="token imports maybe-class-name">SharedBuffer</span><span class="token imports"> </span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'@ammarahmed/react-native-workers'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> buf </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">SharedBuffer</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'frame'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1920</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1080</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">4</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">buf</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">withLock</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// write bytes here in the worker; the UI reads the same memory — no message copy</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></div></code></pre></div></div>
<figure class="figure_hngk" style="width:280px"><div class="bezel_ktx_"><div class="screen_XbMa"><video class="media_V7ZR" src="/react-native-workers/img/video/sensor.mp4" aria-label="A live waveform drawn from a ring buffer in shared memory; the producer rate jumps from 163Hz to 800Hz while the screen keeps reading at about 25Hz" autoplay="" loop="" muted="" playsinline="" preload="metadata"></video></div></div><figcaption class="caption_Fis_">A worker filling a ring buffer in shared memory. Halfway through, its sample rate is raised to 1000&nbsp;Hz — the reader carries on at ~25&nbsp;Hz and simply draws whatever is in the ring, with <code>withLock</code> keeping the window it copies consistent.</figcaption></figure>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="reactive-and-definemodule--higher-level-coordination">reactive() and defineModule() — higher-level coordination<a href="https://ammarahm-ed.github.io/react-native-workers/blog/introducing-react-native-workers#reactive-and-definemodule--higher-level-coordination" class="hash-link" aria-label="Direct link to reactive() and defineModule() — higher-level coordination" title="Direct link to reactive() and defineModule() — higher-level coordination" translate="no">​</a></h3>
<p>On top of the store, <a class="" href="https://ammarahm-ed.github.io/react-native-workers/docs/shared-data/reactive-state"><strong><code>reactive()</code></strong></a> gives you a
proxy-based state object that syncs across the boundary — mutate it like a plain object,
and other runtimes see the change (the <a class="" href="https://ammarahm-ed.github.io/react-native-workers/docs/tutorials/note-editor">note-editor tutorial</a>
builds a whole editor on it):</p>
<div class="language-js codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-js codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword module" style="color:#00009f">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:#393A34">{</span><span class="token imports"> reactive</span><span class="token imports punctuation" style="color:#393A34">,</span><span class="token imports"> </span><span class="token imports maybe-class-name">SharedStore</span><span class="token imports"> </span><span class="token imports punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword module" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'@ammarahmed/react-native-workers'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> state </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">reactive</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">SharedStore</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'import'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// observable, cross-runtime</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">state</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">progress</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic">// set from the worker</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">state</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">progress</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">+=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.1</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain">     </span><span class="token comment" style="color:#999988;font-style:italic">// mutate; the UI's subscription fires</span><br></div></code></pre></div></div>
<p>And <strong><code>defineModule()</code></strong> is a small, fully-typed RPC bridge for when you'd rather call
methods than hand-roll a message protocol: from one contract you get to call host
methods from the worker, emit events <strong>both</strong> ways, and share reactive <code>state</code> — all
typed end to end, with a <code>dispose()</code> to tear it down. The
<a class="" href="https://ammarahm-ed.github.io/react-native-workers/docs/rpc/define-module">defineModule guide</a> has the full shape.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="running-a-workers-js-on-another-thread-experimental">Running a worker's JS on <em>another</em> thread (experimental)<a href="https://ammarahm-ed.github.io/react-native-workers/blog/introducing-react-native-workers#running-a-workers-js-on-another-thread-experimental" class="hash-link" aria-label="Direct link to running-a-workers-js-on-another-thread-experimental" title="Direct link to running-a-workers-js-on-another-thread-experimental" translate="no">​</a></h2>
<p>Everything above moves <em>data</em> between runtimes. The newest piece moves the
<strong>thread</strong> instead.</p>
<p>A worker's runtime is normally pinned to its own thread. The experimental
<code>Thread</code> API lets that same runtime temporarily execute somewhere else — the
main/UI thread, or a background thread you create:</p>
<div class="language-js codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-js codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">enableMultiThreadingExperimental</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> codec </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token maybe-class-name">Thread</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">create</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'codec'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> codec</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">run</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> pixels </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">decodeFrame</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">bytes</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic">// on the 'codec' thread</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> summary </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">analyze</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">pixels</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token maybe-class-name">Thread</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">main</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">run</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">applyToNativeView</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">summary</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic">// on the main thread</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></div></code></pre></div></div>
<p>The important part is what <em>isn't</em> happening: there's no second runtime and
nothing is serialized. The callback is a plain closure that keeps every binding
it captured — same variables, same objects, same identity. Only the OS thread
executing the runtime changes.</p>
<div class="language-js codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-js codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">let</span><span class="token plain"> progress </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> codec</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">run</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> progress </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">50</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// same variable, no message passing</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token console class-name">console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">log</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">progress</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain">                     </span><span class="token comment" style="color:#999988;font-style:italic">// 50</span><br></div></code></pre></div></div>
<p>That works because every entry into a worker's runtime — its event loop, timers,
native module callbacks, the debugger, and <code>Thread.run</code> — now goes through a
per-worker lock, so exactly one thread is ever inside the runtime. It is <strong>thread
affinity, not parallelism</strong>: a <code>run()</code> body is atomic, and a slow one blocks the
worker (or janks the UI, if it's on <code>Thread.main</code>).</p>
<p>Promises settle back on the worker's <em>own</em> thread, so <code>await</code> always returns you
where you started and thread changes only ever happen inside a <code>run()</code> callback —
the single-threaded mental model survives.</p>
<p>It's off by default and gated per worker behind
<code>enableMultiThreadingExperimental()</code>. The <a class="" href="https://ammarahm-ed.github.io/react-native-workers/docs/guides/threads">Thread hopping
guide</a> covers the rules before you ship it — and when a
plain <code>Worker</code> or a <a class="" href="https://ammarahm-ed.github.io/react-native-workers/docs/guides/ui-worker"><code>UIWorker</code></a> is the better tool.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="messaging-cloning-and-nesting">Messaging, cloning, and nesting<a href="https://ammarahm-ed.github.io/react-native-workers/blog/introducing-react-native-workers#messaging-cloning-and-nesting" class="hash-link" aria-label="Direct link to Messaging, cloning, and nesting" title="Direct link to Messaging, cloning, and nesting" translate="no">​</a></h2>
<p>Data crosses the boundary via <strong>structured clone</strong> — objects, arrays, <code>Date</code>, typed
arrays, <code>ArrayBuffer</code>, and cycles all survive (a few types don't yet; see below), all
covered in the <a class="" href="https://ammarahm-ed.github.io/react-native-workers/docs/guides/messaging">messaging guide</a>. <strong>Nested workers</strong> work — a
worker can spawn its own children and relay their results — and RN <strong>device events</strong> are
forwarded into workers (<a class="" href="https://ammarahm-ed.github.io/react-native-workers/docs/guides/native-events">native events guide</a>), so a worker
can listen for the same app-level events the main thread sees — the
<a class="" href="https://ammarahm-ed.github.io/react-native-workers/docs/tutorials/sensor-stream">sensor-stream tutorial</a> leans on exactly that.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="debugging">Debugging<a href="https://ammarahm-ed.github.io/react-native-workers/blog/introducing-react-native-workers#debugging" class="hash-link" aria-label="Direct link to Debugging" title="Direct link to Debugging" translate="no">​</a></h2>
<p>Workers aren't a black box:</p>
<ul>
<li class=""><strong><code>console.*</code> inside a worker is forwarded to your main logs</strong>, tagged per worker
(<code>[Worker:&lt;name&gt;]</code>), so you see worker output where you already look.</li>
<li class=""><strong>Every background worker is its own DevTools target</strong> — breakpoints and stepping,
no flag required. A <code>UIWorker</code> is the exception: it runs on the main thread, where
a debugger pause freezes the whole app, so it takes an explicit
<code>inspectable: true</code>.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="tested-against-a-lot-of-react-native-and-expo">Tested against a lot of React Native and Expo<a href="https://ammarahm-ed.github.io/react-native-workers/blog/introducing-react-native-workers#tested-against-a-lot-of-react-native-and-expo" class="hash-link" aria-label="Direct link to Tested against a lot of React Native and Expo" title="Direct link to Tested against a lot of React Native and Expo" translate="no">​</a></h2>
<p>react-native-workers is New-Architecture-native: bridgeless, Hermes, JSI throughout.
It supports <strong>React Native 0.81.4+</strong> on <strong>iOS and Android</strong>.</p>
<p>Depending on private React Native and Expo internals is a fact of life for a library
like this — so there's a compatibility matrix that scaffolds a fresh app against each
version and compiles the native code, on every relevant release:</p>
<ul>
<li class=""><strong>React Native</strong> 0.81, 0.82, 0.83, 0.84, 0.85, 0.86, <code>latest</code>, and the <code>next</code>
release candidate.</li>
<li class=""><strong>Expo SDK</strong> 54, 55, 56, 57, and <code>latest</code>.</li>
</ul>
<p>On top of that, the example app carries an on-device conformance suite and a
benchmark screen — both of which you can run yourself from the
<a href="https://github.com/ammarahm-ed/react-native-workers/tree/main/example" target="_blank" rel="noopener noreferrer" class="">example app</a>:</p>
<div class="showcase_AzwL"><figure class="figure_hngk" style="width:262px"><div class="bezel_ktx_"><div class="screen_XbMa"><img class="media_V7ZR" src="https://ammarahm-ed.github.io/react-native-workers/img/screens/tests.webp" alt="The example app's test suite screen showing 76 of 76 tests passed, covering workers, native modules, the RPC bridge and the shared primitives" loading="lazy"></div></div><figcaption class="caption_Fis_">The in-app suite on an iPhone 17 Pro simulator: 76/76, covering nested workers, native modules, device events, the RPC bridge and every shared primitive.</figcaption></figure><figure class="figure_hngk" style="width:262px"><div class="bezel_ktx_"><div class="screen_XbMa"><img class="media_V7ZR" src="https://ammarahm-ed.github.io/react-native-workers/img/screens/benchmarks.webp" alt="Benchmark results including 0.016ms per message round-trip, 143MB/s typed-array transfer, SharedValue writes at 0.091 microseconds and setIn being 2x cheaper than messaging" loading="lazy"></div></div><figcaption class="caption_Fis_">The benchmark screen, measured on the same device: message round-trips, bulk transfer, and where each shared primitive actually wins.</figcaption></figure></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-alpha-means">What "alpha" means<a href="https://ammarahm-ed.github.io/react-native-workers/blog/introducing-react-native-workers#what-alpha-means" class="hash-link" aria-label="Direct link to What &quot;alpha&quot; means" title="Direct link to What &quot;alpha&quot; means" translate="no">​</a></h2>
<p>The core is implemented and tested on both platforms, and the API is close to what I
want it to be — but this is a <code>1.0.0-alpha</code>, so:</p>
<ul>
<li class=""><strong>Release builds need one extra build step.</strong> In development Metro serves each
worker bundle and there's nothing to set up; a release build compiles them ahead
of time and ships them inside the app, which means wiring the bundling step into
Xcode/Gradle — see <a class="" href="https://ammarahm-ed.github.io/react-native-workers/docs/guides/bundling">Bundling for release</a>. <strong>Inline
workers</strong> need no build setup at all.</li>
<li class=""><strong>Structured clone</strong> doesn't yet copy <code>Map</code>, <code>Set</code>, <code>RegExp</code>, <code>Error</code>, or <code>BigInt</code>.</li>
<li class=""><strong><code>Thread</code> is experimental</strong>, off unless you call
<code>enableMultiThreadingExperimental()</code>, and its shape may change. There is no
synchronous <code>runSync</code> on purpose — see <a class="" href="https://ammarahm-ed.github.io/react-native-workers/docs/guides/threads#the-rules">the rules</a>.</li>
<li class="">The API may still shift before <code>1.0.0</code>.</li>
</ul>
<p>If you hit an edge, that's exactly the feedback this release is for.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="where-its-going">Where it's going<a href="https://ammarahm-ed.github.io/react-native-workers/blog/introducing-react-native-workers#where-its-going" class="hash-link" aria-label="Direct link to Where it's going" title="Direct link to Where it's going" translate="no">​</a></h2>
<p>A couple of things on the near horizon:</p>
<ul>
<li class=""><strong>A wider clone subset</strong> — <code>Map</code>, <code>Set</code>, <code>RegExp</code> and <code>Error</code> across the
boundary, so fewer values need reshaping before you send them.</li>
<li class=""><strong>Transferables</strong> — a <code>postMessage(msg, [transfer])</code> transfer-list on top of the
shared-memory primitives that already exist, for true zero-copy handoff.</li>
<li class=""><strong>Settling <code>Thread</code></strong> — the experimental API either graduates or changes shape
based on what people hit using it.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="try-it">Try it<a href="https://ammarahm-ed.github.io/react-native-workers/blog/introducing-react-native-workers#try-it" class="hash-link" aria-label="Direct link to Try it" title="Direct link to Try it" translate="no">​</a></h2>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">npm</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">install</span><span class="token plain"> @ammarahmed/react-native-workers@alpha</span><br></div></code></pre></div></div>
<p>Then the <a class="" href="https://ammarahm-ed.github.io/react-native-workers/docs/installation">Installation guide</a> and the
<a class="" href="https://ammarahm-ed.github.io/react-native-workers/docs/quick-start">Quick start</a> get you running your first worker in a few minutes,
and the <a class="" href="https://ammarahm-ed.github.io/react-native-workers/docs/intro">guides</a> go deep on native modules, UIWorker, and shared state.
Every feature above has a runnable example — browse them all under
<a class="" href="https://ammarahm-ed.github.io/react-native-workers/docs/examples">Examples</a>, or clone the
<a href="https://github.com/ammarahm-ed/react-native-workers/tree/main/example" target="_blank" rel="noopener noreferrer" class="">example app</a> and
run it.</p>
<p>If you're building something that would genuinely benefit from real threads — a heavy
importer, an on-device pipeline, a game loop, an editor that shouldn't stutter — this
is a great time to try it and tell me what breaks or what's missing.</p>
<ul>
<li class="">⭐ <a href="https://github.com/ammarahm-ed/react-native-workers" target="_blank" rel="noopener noreferrer" class="">Star it on GitHub</a></li>
<li class="">🐛 <a href="https://github.com/ammarahm-ed/react-native-workers/issues" target="_blank" rel="noopener noreferrer" class="">Open an issue</a></li>
<li class="">📚 <a class="" href="https://ammarahm-ed.github.io/react-native-workers/docs/intro">Read the docs</a></li>
</ul>
<p>More threads, less jank. Let's see what you build.</p>]]></content:encoded>
            <category>release</category>
            <category>announcement</category>
            <category>alpha</category>
        </item>
    </channel>
</rss>