Optimizing with FastImage once the built-in Image component runs into limits
A product list with a hundred thumbnails or a feed full of high-resolution photos quickly exposes the limits of React Native's built-in Image component, which lacks aggressive caching. FastImage closes that gap with native disk and memory caching logic. This article covers the limits of the built-in component and how to configure FastImage in practice.
Table of Contents
- 1. Limits of the built-in Image component
- 2. How FastImage solves the caching problem
- 3. Cache control strategies in detail
- 4. Prioritization in long lists and feeds
- 5. Preloading for better perceived performance
- 6. Controlling memory usage and cache limits
- 7. Cache invalidation and manual clearing
- 8. Combining FastImage sensibly with FlatList and FlashList
- 9. When the built-in Image component is actually enough
- 10. Summary
- 11. FAQ
1. Limits of the built-in Image component
React Native's built-in Image component uses different, fairly simple caching mechanisms on iOS and Android that offer little in the way of configuration. On iOS it falls back on the system-wide URL cache, shared with countless other requests across the app, leaving little control over how long a specific image actually stays cached.
In practice this means images in long lists, say a product catalog or a social feed, visibly reload on rescroll even though they were displayed moments earlier. For a handful of images on simple screens that's not an issue, but for large image galleries it has a direct impact on the app's perceived performance.
2. How FastImage solves the caching problem
FastImage replaces the native image loading logic with SDWebImage on iOS and Glide on Android, two established, specialized libraries that ship with a two-tier cache spanning memory and disk out of the box. An image that has already been loaded once can be shown instantly from memory or disk on the next render, without another network request.
FastImage also automatically deduplicates parallel requests for the same image URL: if several list items rendered at the same time request the same image, only a single network request actually goes out, while every waiting component receives the result of that same request once it arrives.
import FastImage from 'react-native-fast-image';
function ProductThumbnail({ uri }: { uri: string }) {
return (
<FastImage
style={{ width: 120, height: 120, borderRadius: 8 }}
source={{
uri,
priority: FastImage.priority.normal,
cache: FastImage.cacheControl.immutable,
}}
resizeMode={FastImage.resizeMode.cover}
/>
);
}
3. Cache control strategies in detail
FastImage offers three cache control modes that precisely determine how HTTP cache headers get handled. The immutable mode assumes an image under the same URL never changes, which is ideal for product images with unique, versioned filenames, and delivers the highest cache hit rate without any repeated header check.
The web mode fully respects the Cache-Control and Expires headers sent by the server, which suits content that can occasionally change, such as user avatars. The cacheOnly mode blocks every network request and only returns already cached images, which fits offline views where no fresh download is desired.
4. Prioritization in long lists and feeds
The priority property controls which images get loaded first when many requests are pending at once, for example while fast-scrolling through a FlatList. Images visible in the current viewport should get high priority, while images outside the visible area that are only preloaded can be assigned low priority.
This prioritization noticeably reduces time-to-visible-image, because the native loading queue works through the more urgent requests first instead of strictly loading every image in render order. For very long lists, it's also worth skipping preloading altogether for images far outside the visible area, avoiding unnecessary network and memory usage.
5. Preloading for better perceived performance
FastImage exposes a static preload method that loads a list of image URLs ahead of time, before the corresponding components are even rendered. That's particularly useful for the next step in a known user flow, for example preloading detail images as soon as a product list is displayed.
It matters to use preloading in a targeted, bounded way instead of loading every image in an app in the background indiscriminately. Uncontrolled preloading can block available network throughput for actually visible content and unnecessarily drive up the app's memory footprint.
// Preload the next detail images as soon as the list becomes visible
useEffect(() => {
const preloadTargets = products.slice(0, 5).map((p) => ({ uri: p.detailImageUrl }));
FastImage.preload(preloadTargets);
}, [products]);
6. Controlling memory usage and cache limits
An aggressive image cache can itself become a performance problem if left unbounded, especially on older devices with limited memory. FastImage largely leaves the concrete size limits to the native SDWebImage and Glide libraries, both of which ship with sensible defaults but can be fine-tuned through native configuration if needed.
In practice it's usually enough to keep the defaults and instead control image size at the source: a thumbnail should not be loaded at the original's full resolution, but should already arrive pre-scaled via an image server or CDN transformation, so the cache isn't filled unnecessarily with oversized data.
7. Cache invalidation and manual clearing
For cases where an image under the same URL genuinely changes, for example an updated user profile picture, the immutable mode falls short because it ignores exactly that change. Better options are a cache-busting parameter in the URL or an explicit call to FastImage.clearMemoryCache and FastImage.clearDiskCache after a successful upload.
A full cache reset should be used sparingly, since it discards every previously cached image and briefly generates increased network traffic again. For isolated changes, a cache-busting parameter that only affects the specific URL is usually the more targeted and resource-friendly solution.
8. Combining FastImage sensibly with FlatList and FlashList
FastImage delivers its biggest benefit combined with a FlatList or the more performant FlashList, since fast scrolling mounts and unmounts many image components at once. Without a disk cache, every image that becomes visible again would have to be fully reloaded from the network, causing visible reloading during fast scrolling.
It's also worth setting the list's getItemLayout property when image sizes are known in advance, so the list itself renders more efficiently and FastImage doesn't trigger extra, unnecessary loading cycles from frequent re-layouts.
9. When the built-in Image component is actually enough
Not every app needs FastImage: with just a few static images per screen, say a logo or a single hero image, React Native's native image loading logic usually shows no noticeable downside, and the extra native dependency barely pays off in such cases.
But once more than a handful of network images are visible at once, users frequently navigate back and forth between views showing the same images, or a scrollable list of thumbnails sits at the core of the app, the benefit of FastImage clearly outweighs the added complexity.
| Cache mode | Behavior | Typical use case | Main limitation |
|---|---|---|---|
| immutable | Ignores HTTP cache headers, maximum hit rate | Product images with versioned, unique URLs | Changes under the same URL go undetected |
| web | Respects Cache-Control and Expires headers | Content that occasionally changes, e.g. avatars | Cache hit rate depends on server headers |
| cacheOnly | No network request, only already cached images | Offline views without a fresh download | New, not-yet-cached images stay invisible |
| priority.high | Preferred processing in the loading queue | Images visible in the current viewport | Too many high-priority requests cancel out the effect |
| priority.low | Deferred processing in the loading queue | Preloaded images outside the visible area | Can be significantly delayed on weak connections |
Mironsoft
React Native app development and Magento integration
A mobile app for the Magento shop that actually runs smoothly?
We build React Native apps cleanly connected to the Magento REST or GraphQL API, from the first line of code to publishing on the App Store and Google Play.
App Concept
Plan the architecture and feature scope of a Magento-connected app together.
Magento API Integration
Cleanly connect product catalog, cart, and checkout to the shop API.
Store Publishing
Guide the App Store and Google Play release process without pitfalls.
10. Summary
Image Caching with FastImage: Key Takeaways
Built-in component has limits
The built-in Image component offers little configurable caching and causes visible reloading in long lists.
FastImage uses SDWebImage and Glide
Two-tier memory and disk cache plus automatic deduplication of parallel requests for the same URL.
Set priority and cache mode deliberately
Visible images get high priority, versioned URLs benefit from the immutable cache mode.
Use preloading sparingly
Targeted preloading for the next user step pays off, uncontrolled preloading blocks network and memory.