iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

Image

The <Image> component renders local, bundled, or remote images. Get the resize mode, caching, and resolution variants right and your UI feels native; ignore them and you ship a janky grid that drops frames on every scroll.

sources, resizeMode, FastImage, caching

EXAMPLE
import { Image, ImageBackground, View, Text, ScrollView, StyleSheet } from 'react-native';

// 1) Local image — bundled with the app
<Image source={require('./assets/logo.png')} style={{ width: 120, height: 40 }} />

// React Native auto-picks logo.png / logo@2x.png / logo@3x.png based on device scale.

// 2) Remote image
<Image
    source={{ uri: 'https://cdn.example.com/avatar/42.jpg' }}
    style={{ width: 80, height: 80, borderRadius: 40 }}
/>

// You MUST specify width and height for network images, or layout breaks.

// 3) Multi-resolution remote images
<Image
    source={{
        uri: 'https://cdn.example.com/cover.jpg',
        cache: 'force-cache',          // iOS: prefer cache; check network only if missing
        headers: { Authorization: `Bearer ${token}` },
    }}
    style={{ width: '100%', aspectRatio: 16 / 9 }}
/>

// 4) resizeMode — how the image fits into its box
//   'cover'   default — fill the box, crop overflow (most common for hero images)
//   'contain' fit entirely inside, may leave bars
//   'stretch' stretches/squashes to exactly the box — almost never what you want
//   'center'  no scaling, centered
//   'repeat'  tile (iOS only)

<Image source={...} style={styles.hero} resizeMode="cover" />
<Image source={...} style={styles.thumb} resizeMode="contain" />

// 5) Placeholder + onLoad/onError
import { useState } from 'react';
function Avatar({ uri }) {
    const [loaded, setLoaded] = useState(false);
    const [error, setError]   = useState(false);

    if (error) return <View style={styles.fallback}><Text>?</Text></View>;

    return (
        <View>
            {!loaded && <View style={[styles.avatar, styles.placeholder]} />}
            <Image
                source={{ uri }}
                style={[styles.avatar, !loaded && styles.hidden]}
                onLoad={() => setLoaded(true)}
                onError={() => setError(true)}
            />
        </View>
    );
}

// 6) ImageBackground — children render on top of an image
<ImageBackground
    source={{ uri: 'https://…/hero.jpg' }}
    style={{ height: 220, justifyContent: 'flex-end', padding: 16 }}
    imageStyle={{ borderRadius: 12 }}
>
    <Text style={{ color: 'white', fontSize: 24, fontWeight: '600' }}>
        Sale this week
    </Text>
</ImageBackground>

// 7) Aspect ratio without measuring
<Image source={{ uri }} style={{ width: '100%', aspectRatio: 4 / 3 }} />

// For unknown remote dimensions, fetch them first:
import { Image as RNImage } from 'react-native';
RNImage.getSize(uri, (w, h) => setRatio(w / h));

// 8) Prefetching
import { Image } from 'react-native';
await Image.prefetch('https://cdn.example.com/next-page-hero.jpg');
await Image.queryCache(['https://cdn.example.com/a.jpg', 'https://cdn.example.com/b.jpg']);
await Image.clearMemoryCache();
await Image.clearDiskCache();

// 9) FastImage (community) — better caching, priority, progressive JPEG
import FastImage from 'react-native-fast-image';
<FastImage
    style={{ width: 200, height: 200 }}
    source={{
        uri: 'https://cdn.example.com/large.jpg',
        priority: FastImage.priority.high,
        cache:    FastImage.cacheControl.immutable,
    }}
    resizeMode={FastImage.resizeMode.cover}
    onLoadStart={() => /* show spinner */ undefined}
/>

// FastImage uses native SDWebImage (iOS) / Glide (Android) under the hood —
// dramatically smoother in long lists. Drop-in for high-throughput screens.

// 10) Expo image (modern alternative)
import { Image as ExpoImage } from 'expo-image';
<ExpoImage
    source={{ uri: 'https://cdn.example.com/photo.jpg' }}
    style={{ width: 200, height: 200 }}
    contentFit="cover"
    transition={300}                    // crossfade in 300ms
    placeholder={require('./blurhash')} // BlurHash or thumb
    cachePolicy="memory-disk"
/>

// 11) Performance checklist for image-heavy lists
//   ✓ Reach for FastImage or expo-image for FlatList rows
//   ✓ Size images server-side to the rendered dimensions (e.g. ?w=400)
//   ✓ Use BlurHash / thumbhash placeholders, not loading spinners
//   ✓ Set width AND height (not just one) so layout doesn't shift
//   ✓ Memoize row components and use getItemLayout for fixed-size rows
//   ✓ Lazy-load below-the-fold images (initialNumToRender)
//   ✓ Use windowSize to control how many rows stay mounted

// 12) Loading from local file system
import RNFS from 'react-native-fs';
const path = `${RNFS.DocumentDirectoryPath}/cached/avatar.jpg`;
<Image source={{ uri: `file://${path}` }} style={{ width: 80, height: 80 }} />

// 13) SVG
// react-native doesn't render SVG out of the box. Use react-native-svg + react-native-svg-transformer
// to import .svg files as components, or load via expo-image / WebView for static SVGs.

// 14) Common bugs
//   • Network image without dimensions → renders 0x0 and 'disappears'
//   • CDN URL with query params changes per render → cache miss every time → wrap with useMemo
//   • resizeMode 'stretch' looks wrong almost always — use 'cover' or 'contain'
//   • Image flicker on re-render → key on the URI, avoid replacing source object identity unnecessarily
//   • Slow long lists → upgrade to FastImage or expo-image
//   • Android low memory crash on big images → ask the server for a thumb URL, never load full-res into a 60-row list

Why it matters

For one-off images the built-in Image is fine; for any list scroll, upgrade to FastImage or expo-image. Always size images server-side to the rendered dimensions, set explicit width and height, and use a BlurHash placeholder — the layout-shift you avoid is the difference between “nice app” and “native”.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
import { Image } from 'react-native';
<Image
    source={{ uri: 'https://picsum.photos/300' }}
    style={{ width: 300, height: 300 }}
/>
Try it Yourself »

Discussion

Loading…