# react-native-nitro-fetch, a native fetch for React Native

> react-native-nitro-fetch replaces the built-in fetch with a native stack, Cronet on Android and URLSession on iOS, and adds two things the built-in cannot do, prefetching that survives until the next app launch and a token refresh that runs before the JavaScript bundle is even loaded. It is built on Nitro Modules and the repository is a monorepo of three packages.

**margelo/react-native-nitro-fetch** — A super-fast network fetching library for React Native with prefetching support

- Repository: https://github.com/margelo/react-native-nitro-fetch
- Website: https://fetch.margelo.com
- Stars: 1,027 · Forks: 56
- Language: C++
- License: MIT
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/margelo-react-native-nitro-fetch

## The transport changes, the call signature does not

react-native-nitro-fetch is a general purpose network fetching library for React Native that can be used as a drop-in replacement for the built-in fetch method, and it also provides extra features that the built-in has no way of offering, prefetching and workletized mappers. The transport is the substance. The fast HTTP stack uses Cronet on Android and URLSession on iOS, which means the network behaviour comes from the same engines that Chromium and the Apple platforms use rather than from the React Native bridge, and the implementation language of the repository is C++. On top of that it supports HTTP/1, HTTP/2 and HTTP/3 over QUIC, plus Brotli and a disk cache. The whole thing is powered by Nitro Modules, which is the framework that gives the library its native bridge, and the homepage is fetch.margelo.com. The audience is a React Native team whose startup or navigation is blocked on a network round trip and who want that fixed without rewriting their data layer.

## Two installs, and a hard floor on React Native

Installation is npm, and there are two packages because the native bridge comes from Nitro Modules.

```bash
npm i react-native-nitro-fetch react-native-nitro-modules
```

The README is explicit that Nitro Modules requires react-native 0.75 or higher, which is the version floor to check first, because it comes from the framework rather than from this library. WebSockets are an optional extra, shipped in a companion socket package, and they need the text decoder package as well since it is a peer dependency of the websockets package.

```bash
npm i react-native-nitro-websockets react-native-nitro-text-decoder
```

The documentation for that path is a separate file, docs/websockets.md, and the README points at both a screen in the example application and the token refresh section for the prewarm flow, which suggests the WebSocket support is the more involved half of the library. The example application in the repository is a full React Native project with android and ios directories, its own Metro and Babel configuration, a Jest harness setup, and a test-utils directory, so the library ships with a working app you can run rather than a snippet.

## Prefetching a URL before the screen that needs it

The in-JavaScript prefetch is the simple case. You call prefetch with a URL and a prefetch key in the headers, and the result stays cached for the next actual fetch call, which is the shape you want when you are about to navigate to a screen that will request that data.

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

await prefetch('https://httpbin.org/uuid', {
  headers: { prefetchKey: 'uuid' }
})
```

The matching fetch on the new screen passes the same prefetch key, and the response tells you what happened through a header.

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

const res = await fetch('https://httpbin.org/uuid', {
  headers: { prefetchKey: 'uuid' }
})
console.log('prefetched header:', res.headers.get('nitroPrefetched'))
```

The nitroPrefetched header is a small but useful detail, because it lets a test or a debug screen confirm that the cache path was actually taken rather than assuming it. The usage in practice is to prefetch shortly before navigating, so the data is hot when the screen mounts.

## Prefetching for the next launch, before the bundle exists

The second prefetch mode is the one the project is really about. prefetchOnAppStart enqueues a prefetch for the next app start, so the request happens in native code while the JavaScript bundle is still loading.

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

await prefetchOnAppStart('https://httpbin.org/uuid', {
  prefetchKey: 'uuid'
})
```

When the app opens again, the fetch for the same key resolves faster because the result is already cached, and the same nitroPrefetched header reports it. The README quotes a figure from the project's own tests, that prefetching alone yielded a roughly 220 millisecond faster time to interactive, and that is a claim from the maintainers rather than an independent measurement, but the mechanism behind it is clear enough to judge for yourself. The catch is that this only helps for data that does not need authentication, because a request made before the JavaScript runs has no access to a token that lives in JavaScript memory. That is exactly the problem the next feature solves.

## Token refresh that runs before the JavaScript bundle

When you use auto-prefetch through prefetchOnAppStart, or a WebSocket prewarm on app start, the native code runs before the JavaScript bundle is evaluated, and that native code has no token. The solution is to register a token refresh configuration, which is persisted in encrypted native storage, and on each cold start the native code calls your refresh URL, maps the response into HTTP headers, and merges them into the auto-prefetches and the WebSocket prewarms.

```ts
import { registerTokenRefresh } from 'react-native-nitro-fetch'

registerTokenRefresh({
  target: 'fetch',
  url: 'https://api.example.com/oauth/token',
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ grant_type: 'client_credentials' }),
  responseType: 'json',
  mappings: [
    { jsonPath: 'access_token', header: 'Authorization', valueTemplate: 'Bearer {{value}}' },
  ],
  onFailure: 'useStoredHeaders',
})
```

The mapping rules are the interesting part. A mapping copies a field from the JSON body into a header, with dot paths supported, and compositeHeaders builds a header from a template and several JSON paths. For a plain text response you set responseType to text and use textHeader with an optional textTemplate. To inject into the prefetch request body rather than its headers you use bodyMappings for a JSON body and formDataMappings for a multipart field, and the README is careful that each mapping applies only to a prefetch whose body it matches, so a body mapping will not synthesise a JSON body on a GET and a form data mapping will not turn a request into multipart. The failure behaviour is explicit too, with onFailure set to useStoredHeaders, the default, meaning the last successful headers from the previous run are reused, or skip, meaning the auto-prefetch and prewarm are skipped entirely when the refresh fails.

## Worklets, mappers and a monorepo of three packages

Beyond networking, the library advertises worklet support for parallel data mapping without blocking the JavaScript thread, which is aimed at transforming a large response off the JS thread. The repository is a monorepo built with bun, and the root package.json names four workspaces: the example application and the three packages react-native-nitro-fetch, react-native-nitro-text-decoder and react-native-nitro-websockets. The scripts show how the work is organised. There is a nitrogen script that runs codegen across the packages, which is the Nitro way of generating the native bindings, and separate release scripts per package driven by release-it, so the three packages version independently. A docs-website directory with its own start and build scripts holds the documentation site, and there is a test-server directory, a skills directory, a patches directory, a turbo.json, a lefthook.yml, and both a bun.lock and a package-lock.json in the tree. The primary language of the repository is C++, which matches a library whose point is to replace a JavaScript implementation with a native one.

## Release rhythm, and what to check before you upgrade

The release history is busy around a single version, with v1.8.1-alpha.0 on 2026-10-12, v1.8.1-alpha.1 on 2026-10-14 and v1.9.0 on 2026-10-15, and the repository is not archived, with a last push after the 1.9.0 tag. Two alphas in three days before a stable release is a normal pattern, and it also tells you that the version you install changes quickly, so pinning matters. The licence is MIT, which is permissive but does require keeping the copyright and permission notice when you redistribute the library. The checks that matter before you upgrade are practical. React Native has to be 0.75 or newer, the fetch and the websockets packages are separate installs, and the native side has to be rebuilt, so a version bump is a native build rather than a JavaScript only bump. The last thing to confirm is what your app does when the token refresh fails at cold start, because with the default behaviour the app proceeds with the headers from the previous successful run, which is usually what you want and occasionally is not.

## Conclusion

react-native-nitro-fetch fits a React Native application whose startup is dominated by a network round trip, since the prefetch and token refresh machinery runs in native code before the bundle is evaluated, and it fits a team that wants to keep the fetch call signature while changing the transport underneath. It does not fit an application that must run on a platform other than Android and iOS, and it does not fit a project that cannot take a dependency on Nitro Modules and a minimum React Native version. Verify three things before adopting it: that your React Native version is 0.75 or newer, since that is the floor Nitro Modules imposes, whether the responses you cache can safely be prefetched before authentication, since the prefetched result is reused by a later fetch, and how the token refresh failure path behaves in your app, because the default is to reuse the headers stored from the previous successful run.

## FAQ

### What is react-native-nitro-fetch?

It is a general purpose network fetching library for React Native that can be used as a drop-in replacement for the built-in fetch method, and it adds prefetching and workletized mappers. It uses Cronet on Android and URLSession on iOS, supports HTTP/1, HTTP/2 and HTTP/3 over QUIC, and is built on Nitro Modules.

### How do I install react-native-nitro-fetch?

Run npm i react-native-nitro-fetch react-native-nitro-modules, since the native bridge comes from Nitro Modules, which requires react-native 0.75 or higher. WebSockets are optional and need npm i react-native-nitro-websockets react-native-nitro-text-decoder.

### How does prefetching work in react-native-nitro-fetch?

The prefetch function caches a URL under a prefetch key given in the headers, and a later fetch with the same key returns the cached result, reporting it through the nitroPrefetched response header. The prefetchOnAppStart function enqueues a prefetch for the next app launch, so the request happens in native code before the JavaScript bundle runs.

### How does the token refresh configuration work?

You register it with registerTokenRefresh, it is persisted in encrypted native storage, and on each cold start the native code calls the refresh URL, maps the response into headers with the mappings, compositeHeaders, textHeader, bodyMappings or formDataMappings options, and merges them into the auto-prefetches. If the refresh fails, onFailure decides between reusing the stored headers from the last successful run, which is the default, and skipping the prefetch entirely.

## Sources

- [License: MIT](https://github.com/margelo/react-native-nitro-fetch/blob/main/LICENSE)
- [margelo/react-native-nitro-fetch on GitHub](https://github.com/margelo/react-native-nitro-fetch)
- [Project website](https://fetch.margelo.com)
- [README](https://github.com/margelo/react-native-nitro-fetch/blob/main/README.md)
- [Releases](https://github.com/margelo/react-native-nitro-fetch/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/margelo-react-native-nitro-fetch
