Magento 2 Experten — Hyvä Theme, Tailwind CSS & SEO aus einer Hand ›

Image Performance with expo-image in React Native

Image Performance with expo-image

~13 Min. Lesezeit Zuletzt aktualisiert am August 8, 2026

ProductCard's <Image source={{{{ uri: imageUrl }}}}> from "React Native for Beginners" uses React Native's built-in Image component – functionally correct, but without caching control and without modern image format support. expo-image replaces it with considerably more performance features.

Installing expo-image

npx expo install expo-image

ProductCard.js: replacing Image with expo-image

components/ProductCard.js
import { memo } from 'react';
import { View, Text, StyleSheet, TouchableOpacity } from 'react-native';
import { Image } from 'expo-image';

const BLURHASH = '|rF?hV%2WCj[ayj[a|j[az_NaeWBj@ayfRayfQfQM{M|azj[azf6fQfQfQIpWXofj[ayj[j[fQayWCoeoeaya}j[ayfQa{oLj?j[WVj[ayayj[fQoff7azayj[ayj[j[ayofayayayj[fQj[ayayj[ayfjj[j[ayjuayj[';

function ProductCard({ name, price, imageUrl, onPress, isFavorite, onToggleFavorite }) {
  return (
    <TouchableOpacity style={styles.card} onPress={onPress}>
      <Image
        source={imageUrl}
        style={styles.image}
        placeholder={{ blurhash: BLURHASH }}
        contentFit="cover"
        transition={200}
        cachePolicy="memory-disk"
      />
      <View style={styles.info}>
        <Text style={styles.name}>{name}</Text>
        <Text style={styles.price}>${price.toFixed(2)}</Text>
      </View>
      <TouchableOpacity style={styles.favoriteButton} onPress={onToggleFavorite}>
        <Text style={styles.favoriteIcon}>{isFavorite ? '♥' : '♡'}</Text>
      </TouchableOpacity>
    </TouchableOpacity>
  );
}

const styles = StyleSheet.create({
  card: {
    flexDirection: 'row',
    padding: 12,
    marginBottom: 8,
    backgroundColor: '#f9fafb',
    borderRadius: 8,
    alignItems: 'center',
  },
  image: { width: 60, height: 60, borderRadius: 4 },
  info: { marginLeft: 12, flex: 1 },
  name: { fontSize: 16, fontWeight: '600' },
  price: { fontSize: 14, color: '#6b7280' },
  favoriteButton: { padding: 8 },
  favoriteIcon: { fontSize: 20, color: '#ef4444' },
});

export default memo(ProductCard);

The key new props in detail

  • source={{imageUrl}} instead of source={{{{ uri: imageUrl }}}}expo-image accepts a plain URL string directly, the uri object wrapping of the built-in Image component is no longer needed.
  • placeholder={{{{ blurhash: BLURHASH }}}} – shows a blurred placeholder (generated from a compact text string, see blurha.sh for creating your own values) WHILE the real image loads, instead of an empty gray rectangle.
  • contentFit="cover" – the direct equivalent of CSS' object-fit, or the built-in Image component's resizeMode prop (see "React Native Reference", the "Background Image" topic), just with clearer naming.
  • transition={{200}} – a smooth fade-in (200ms) instead of an abrupt appearance once the image finishes loading.
  • cachePolicy="memory-disk" – THE decisive performance difference from the built-in Image component, see the next section.

cachePolicy in detail: why it matters

React Native's built-in Image component only caches images UNRELIABLY/platform-dependently – a product you visit multiple times (product list → detail → back → detail again) often reloads its image MULTIPLE times over the network. cachePolicy="memory-disk" enables TWO cache layers:

  • Memory cache: already-decoded images stay in RAM – INSTANT display when re-rendering the same image URL within the same app session.
  • Disk cache: downloaded image data stays saved on the device – no repeat network download needed, even after an app restart, as long as the cache hasn't been cleared.

Bonus: prefetching images with Image.prefetch

For an even smoother experience, images can be preloaded BEFORE they become visible – e.g. right after the product list finishes loading in ProductListScreen:

import { Image } from 'expo-image';

// After the successful fetchProducts() call:
Image.prefetch(items.map((product) => product.imageUrl));

When the user then actually scrolls, the images are often already in the disk cache – FlatList no longer has to wait for the network download, only for the (very fast) cache read.

Tipp: Rule of thumb for image performance: expo-image with cachePolicy="memory-disk" is practically ALWAYS the right choice over the built-in Image component, as soon as an app shows more than one or two images – the switch costs little effort (usually just an import swap plus renaming a few props), the performance gain for repeatedly displayed images (like our product images) is substantial.