React Native Image Fallback with Default Source URLs
Implement react native image fallback using defaultSource and onError to show placeholder images when remote URLs fail on iOS and Android.
React Native's Image component provides two mechanisms for handling image failures: the defaultSource prop, which shows a local asset while the remote image loads, and the onError callback, which fires when the remote image cannot be fetched. Neither mechanism automatically swaps in a fallback URL — you implement that logic yourself.
Using a remote URL from fallback.pics as the fallback eliminates the need to bundle a placeholder image asset in your app. This reduces app size and lets you change the fallback design without an app update.
Component API
React Native Image defaultSource and onError behavior
The defaultSource prop accepts a local asset (required() reference) or a URI object. It displays while the source image is loading. On iOS, defaultSource is shown during the loading period. On Android, defaultSource is not supported for remote images loaded via network request — it only works with local assets. This platform divergence is a common source of bugs.
The onError callback fires when the image fails to load (network error, 404, timeout, or invalid image data). Inside the callback, you update the image source to a fallback URL. Without a guard flag, this can trigger an infinite loop if the fallback URL also fails.
// Basic React Native image fallback with onError
import React, { useState } from 'react';
import { Image } from 'react-native';
const FALLBACK_URL = (width: number, height: number) =>
`https://fallback.pics/api/v1/${width}x${height}/7C3AED/FFFFFF`;
export function RemoteImage({
uri,
width = 200,
height = 200,
...props
}: { uri: string; width?: number; height?: number } & React.ComponentProps<typeof Image>) {
const [errored, setErrored] = useState(false);
return (
<Image
{...props}
source={{ uri: errored ? FALLBACK_URL(width, height) : uri }}
style={[props.style, { width, height }]}
onError={() => {
if (!errored) setErrored(true);
}}
/>
);
} Platform differences
iOS vs Android behavior for remote image fallbacks
On iOS, defaultSource renders a local asset during the network fetch. The fetch happens in the background. If it succeeds, the local asset is replaced by the remote image. If it fails, the onError callback fires and you can update the source. iOS handles this correctly.
On Android, defaultSource is ignored when source is a remote URI. The Image component renders nothing until the network image loads or fails. To show a placeholder on Android during loading, use the loadingIndicatorSource prop (deprecated in some RN versions) or render a separate View with a placeholder image behind the Image component using absolute positioning.
React Native's fast-image library (react-native-fast-image) handles this platform inconsistency more gracefully. It supports onLoadStart, onLoad, onError events consistently across iOS and Android and handles cache control, priority queuing, and CORS headers.
// Platform-safe loading state with absolute-positioned placeholder
import { View, Image, StyleSheet } from 'react-native';
import { useState } from 'react';
export function SafeImage({ uri, width, height, style }: {
uri: string; width: number; height: number; style?: object;
}) {
const [loaded, setLoaded] = useState(false);
const [errored, setErrored] = useState(false);
const fallback = `https://fallback.pics/api/v1/${width}x${height}/E4E4E7/71717A`;
return (
<View style={[{ width, height }, style]}>
{/* Placeholder layer — always visible until loaded or errored */}
{!loaded && !errored && (
<Image
source={{ uri: `https://fallback.pics/api/v1/animated/skeleton/${width}x${height}` }}
style={StyleSheet.absoluteFillObject}
/>
)}
<Image
source={{ uri: errored ? fallback : uri }}
style={{ width, height }}
onLoad={() => setLoaded(true)}
onError={() => { if (!errored) setErrored(true); }}
/>
</View>
);
} Remote fallbacks
Using URL-based fallbacks instead of bundled assets
Bundled placeholder assets (require('./placeholder.png')) increase app size and require an app update to change the fallback design. A remote URL from fallback.pics requires network access but is dynamically generated and can be changed without shipping a new app version.
The tradeoff is that remote fallback URLs require network connectivity. If the device is offline and the primary image fails, the remote fallback also fails. For offline-capable apps, bundle a minimal local fallback asset and use the remote URL as an intermediate state when network is available.
List performance
Image fallback performance in FlatList and SectionList
React Native's FlatList recycles cells as the user scrolls. When a cell goes off-screen and back on-screen, the Image component remounts. If you store the errored state in local component state, the remounted component starts fresh and attempts the failing URL again. This causes repeated network errors in scrolling lists.
Lift the errored state out of the image component and into a cache at the list level, keyed by the image URI. When the image reports an error, record it in the cache. On remount, the component checks the cache first and skips the failing URL immediately.
// URI error cache for FlatList
const imageErrorCache = new Set<string>();
export function CachedFallbackImage({ uri, width, height }: {
uri: string; width: number; height: number;
}) {
const [errored, setErrored] = useState(imageErrorCache.has(uri));
const fallback = `https://fallback.pics/api/v1/${width}x${height}/E4E4E7/71717A`;
return (
<Image
source={{ uri: errored ? fallback : uri }}
style={{ width, height }}
onError={() => {
if (!errored) {
imageErrorCache.add(uri);
setErrored(true);
}
}}
/>
);
} Testing
Testing fallback behavior in simulators and on device
Test the fallback by using an intentionally broken image URL (a 404 endpoint or a local IP that does not exist). In the iOS Simulator, use Network Conditioner to simulate offline or slow network conditions. Android emulator allows network speed throttling in the Extended Controls panel.
Log the onError event with the error object — React Native's Image component passes an event with a nativeEvent that includes an error message. This is useful for distinguishing between 404 errors, network timeouts, and image format errors.
Key takeaways
What to standardize before shipping
- React Native's onError callback is the primary mechanism for fallback — update the image source in state with a guard flag to prevent infinite loops.
- defaultSource only works for local assets on Android; use absolute-positioned placeholder images for cross-platform loading states.
- Remote fallback URLs from fallback.pics reduce app size and allow design updates without app releases, but require network access.
- Cache errored URIs at the list level to prevent repeated network failures when FlatList cells remount on scroll.
- Test fallback behavior with intentionally broken URLs and the simulator's network throttling tools.
Production fallback layer
Use fallback.pics anywhere an image URL is accepted.
Start with one deterministic URL and standardize fallback behavior across your design system.