React Native's fetch() Is Slow. Here's Why And the Fix.

AP
Aditya Pandey
Apr 29, 20266 min read

React Native Nitro Fetch Hero

I was debugging a React Native app last week. Cold start felt sluggish. Not broken, just… slow. That half-second where the screen sits there doing nothing before data appears.

I blamed the API. I blamed the server. I profiled the JS bundle. Everything looked fine on paper.

Then I looked at when my fetch call actually goes out. Not when I write fetch() in code when it physically fires a network request. Turns out there's a massive gap between those two things, and almost nobody talks about it.

The Real Reason React Native fetch Is Slow

Every React Native developer has had this conversation with themselves: "Is it my API? Is it the network? Is it the device?"

It's none of those. It's the architecture.

Here's what actually happens on a cold start. Native code boots up. The JavaScript engine initializes. Your bundle parses and executes. Your root component mounts. React renders your tree. Then, finally, your fetch() call fires.

By the time your app asks for data, the user has already been staring at a blank screen or a skeleton loader for 400-600ms. And that's before the network round trip even starts. You haven't even made a request yet and you've already lost the user's attention.

This isn't a bug. It's just how the JS runtime model works. Your network call can't start until JavaScript is ready, and JavaScript takes time to get ready.

Why the Axios vs Fetch Debate Completely Misses This

Every few months someone writes the definitive "Axios vs fetch in React Native" post. They compare interceptors, error handling, bundle size, response parsing. It's all valid. None of it matters for this specific problem.

Whether you use Axios or native fetch(), both calls go out from the JS thread. Both wait for your bundle to load. Both fire after your component mounts. You're optimizing inside a box when the real problem is the box itself.

Switching from Axios to fetch doesn't fix your cold start. Switching from fetch to Axios doesn't fix it either. You're just rearranging deck chairs.

The actual fix is making your network requests start before JavaScript even wakes up. Which sounds impossible. It isn't anymore.

What react-native-nitro-fetch Actually Does

Margelo just shipped react-native-nitro-fetch, and the core idea is embarrassingly clever in retrospect.

First, the basics: it's a drop-in replacement for React Native's built-in fetch(). Same API, same usage, one import change.

import { fetch } from 'react-native-nitro-fetch'

const res = await fetch('https://your-api.com/home')
const json = await res.json()

That's it. Your existing code works. But under the hood, the HTTP stack is completely different. On Android it uses Cronet the same networking layer that powers Chrome instead of React Native's default. On iOS it uses URLSession properly. You get HTTP/2, HTTP/3 over QUIC, Brotli compression, and disk caching. Stuff you assumed you already had. You didn't.

But the drop-in replacement isn't the interesting part.

The Prefetch Trick That Changes Everything

Here's what actually matters. Nitro Fetch lets native code start your network requests before your JavaScript bundle even loads.

import { prefetchOnAppStart } from 'react-native-nitro-fetch'

await prefetchOnAppStart('https://your-api.com/home', { prefetchKey: 'home' })

You register this once. On every cold start, native code fires that request immediately on process start before React initializes, before your bundle parses, before any JS runs at all. By the time your component mounts and calls fetch(), the response is already sitting in cache, warm and ready.

Their tests showed 220ms faster TTI from prefetching alone. On mobile, 220ms is enormous. That's the difference between an app that feels instant and one that feels like it's thinking.

You can also prefetch right before navigating to a new screen so the data is hot and ready when the screen appears instead of loading after.

import { prefetch } from 'react-native-nitro-fetch'

// Call this before pushing to the next screen
await prefetch('https://your-api.com/profile', {
  headers: { prefetchKey: 'profile' }
})

The Part Most Developers Will Ignore (But Shouldn't)

Nitro Fetch runs inside Worklets, which means you can do your JSON parsing completely off the main JS thread.

import { nitroFetchOnWorklet } from 'react-native-nitro-fetch'

const data = await nitroFetchOnWorklet(
  'https://your-api.com/feed',
  undefined,
  (payload) => {
    'worklet'
    return JSON.parse(payload.bodyString ?? '{}')
  }
)

Fetch happens natively. Parsing happens in a Worklet. Your main JS thread never touches any of it. If you've ever noticed your app drop frames right after a fetch resolves this is why, and this is the fix.

Most developers will skip this because the basic API already feels like an upgrade. Don't skip it.

What It Doesn't Do Yet

Streaming is still in progress. If you need it today, Expo's expo-fetch is the fallback. WebSockets are a separate companion package (react-native-nitro-websockets), though it also comes with its own prewarm support.

It's v1.x and the API can change. Use it in production if you want it's stable enough but pin your version.

Why This Feels Like the MMKV Moment for Networking

React Native has always had a performance ceiling that everyone accepted a little too quietly. The JS bridge was the obvious villain, and the new architecture helped. But the network layer was always slow, always under-optimized, always treated as "just how mobile is."

Nitro Fetch has the same energy as react-native-mmkv when it first dropped. Everyone was using AsyncStorage, mildly frustrated, mildly accepting the slowness as a React Native tax. Then MMKV showed up and made storage feel instant. The frustration disappeared because the constraint disappeared.

The constraint here was "your app can't touch the network until JS is ready." Nitro Fetch removes that constraint. That's not a performance optimization. That's a different mental model for how your app initializes.

688 stars in 8 months. One import change. 220ms back in your users' pockets.

The Axios vs fetch debate was always the wrong conversation. The right question was always: why are we waiting for JavaScript to wake up before asking for data we already know we need?

Nitro Fetch answered it.

React Native fetch() Is Slow Fix It With nitro-fetch (220ms Faster TTI)