DEV Community

Cover image for Leveraging Native HTTP Caching in React Native (and Overriding It When Needed)
Danilo Assis
Danilo Assis

Posted on

Leveraging Native HTTP Caching in React Native (and Overriding It When Needed)

Improving performance in mobile apps is always a balancing act — especially when it comes to data fetching. You want to keep things fast, avoid unnecessary API calls, and ensure your users always see the most up-to-date information.

Recently, I ran into this exact challenge while working on a React Native app. I had a screen that fetched a list of specific entity based on a user ID. The data didn't change often, and yet every time the screen loaded, we made a fresh network call.

So, can we cache this response natively and skip the request unless it's really needed? The answer: yes — and React Native already gives us what we need.

Let’s walk through what I did.

The Problem

Imagine this:

fetch(`http://api.example.com/entity/123`)
Enter fullscreen mode Exit fullscreen mode

You make this call when a user opens the app or navigates to a screen. Even if the data hasn't changed, the app hits the backend every time. This not only slows down the UI but also increases server load.

React Native, under the hood, uses:

  • OkHttp on Android
  • NSURLSession on iOS

Both of these support HTTP caching — meaning you can cache GET responses natively based on headers like Cache-Control.

But by default, React Native doesn't configure OkHttp for disk caching. So we first needed to get our backend and frontend talking the same cache language.

Step 1: Let the Backend Control the Cache

We updated our API to include a proper Cache-Control header:

Cache-Control: public, max-age=3600, must-revalidate
Enter fullscreen mode Exit fullscreen mode

What does that mean?

  • public: this response can be cached by clients and shared caches (like proxies or CDNs)

  • max-age=3600: cache is fresh for 1 hour

  • must-revalidate: after 1 hour, the client must check with the server before reusing it

That tells native clients like OkHttp and NSURLSession: “You can safely cache this response for an hour.”

Step 2: Overriding the Cache When Needed

Now here’s the twist.

Sometimes, we wanted to force a fresh request, even if the cache was still valid — like when:

  • The app is reopened
  • The user manually refreshes the screen
  • We suspect stale data for any reason

We solved this by adding a Cache-Control: no-cache header on demand:

fetch('http://api.example.com/entity/123', {
  method: 'GET',
  headers: {
    'Cache-Control': 'no-cache',
  },
})
  .then(response => response.json())
  .then(data => {
    console.log('Fresh response:', data)
  })
  .catch(error => {
    console.error('Fetch error:', error)
  })
Enter fullscreen mode Exit fullscreen mode

Final Thoughts

Caching can feel like a black box — especially in mobile apps — but understanding how native caching works under the hood unlocks a lot of power.

By:

  • Letting the backend control cache timing
  • Letting React Native honor the native cache
  • And providing a clean way to override it when needed

We got the best of both worlds: performance + flexibility.

This flow took a bit to learn, but once everything clicked, it became one of those “ohhh, this is super useful” moments.

Let me know if you want a full code example or a working demo!

Photo of Wouter R in Unsplash

Top comments (0)