In expo-image, what do the cachePolicy and placeholder props control, and how would you use them for a list of receipt thumbnails?
answer
- where the image is cached
- default is disk
- memory-disk for hot lists
- blurhash or thumbhash placeholder
- cacheKey for changing URLs
basics
~20 scachePolicy chooses where expo-image caches a loaded image: 'none', 'disk' (the default), 'memory' or 'memory-disk'. placeholder shows something, such as a blurhash, until the real image loads. For signed receipt URLs, set a stable cacheKey too.
solid answer
~40 s`Image` from `expo-image` caches natively. `cachePolicy` takes `'none'`, `'disk'` (default), `'memory'` or `'memory-disk'`; memory is fastest for images redrawn often, but the docs warn it may be purged quickly to avoid running out of memory, so `'memory-disk'` suits a scrolling list. `placeholder` renders while the source loads; it can be another image or a hash such as `{ blurhash }` or `{ thumbhash }`. Its fit defaults to `'scale-down'`, not the `'cover'` of `contentFit`, so I set `placeholderContentFit` to match and avoid a jump. `transition` cross-fades in, and `recyclingKey` stops a recycled cell from flashing the previous receipt. Receipt images behind signed URLs change their query string whenever the backend re-signs them, so I pass a stable `cacheKey` in the source; otherwise the URL is the key and every refresh misses the cache.
code
tsx · 18 linesimport { Image } from 'expo-image';
type Props = { id: string; signedUrl: string; blurhash: string };
export function ReceiptThumb({ id, signedUrl, blurhash }: Props) {
return (
<Image
source={{ uri: signedUrl, cacheKey: `receipt-${id}` }}
placeholder={{ blurhash }}
contentFit="cover"
placeholderContentFit="cover"
transition={150}
cachePolicy="memory-disk"
recyclingKey={id}
style={{ width: 72, height: 96 }}
/>
);
}go deeper
Recall that expo-image caches on disk by default, that cachePolicy picks where, and that placeholder shows something while loading.
Explain the four cache policies, memory being purgeable, and the placeholder details: hash placeholders, placeholderContentFit and transition.
Fix real list problems: signed URLs defeating the cache with cacheKey, recycled cells flashing old images with recyclingKey, and clearing caches on sign-out.
Set an image strategy for the app: which component, which cache policy per surface, and what may persist on disk for privacy.
## Why expo-image `expo-image` provides an `Image` component backed by native image libraries, with **built-in disk and memory caching**, placeholders, transitions and `contentFit`. For a receipt list, three concerns meet: loading fast, not flashing blank cells while scrolling, and not re-downloading images whose URL changes. ## cachePolicy `cachePolicy` decides **whether and where** a loaded image is cached: | value | behaviour | |---|---| | `'none'` | not cached at all | | `'disk'` (**default**) | read from the disk cache if present, otherwise downloaded and stored on disk | | `'memory'` | kept in memory; may be purged quickly to avoid out-of-memory conditions | | `'memory-disk'` | memory first, with the disk cache as fallback | Guidance: - a **scrolling list** where the same thumbnails reappear benefits from `'memory-disk'`; - a **large one-off image**, such as a full-resolution receipt opened once, is fine with the default `'disk'`; - **sensitive images that must not persist** on disk can use `'none'`, trading speed for not leaving copies. ## Prefetch and cache maintenance - **`Image.prefetch(urls, options)`** downloads images before they are displayed; its `cachePolicy` option defaults to `'memory-disk'` and it accepts `headers` for authenticated URLs. - **`Image.clearMemoryCache()`** and **`Image.clearDiskCache()`** empty the caches, for example on sign-out, so another user of the device does not see cached receipts. - **`Image.getCachePathAsync(cacheKey)`** returns the disk path of a cached image, or `null` when it is not cached. ## The cache key By default the **`uri` is the cache key**. Receipt images served through **signed URLs** get a new query string whenever the backend re-signs them, so each refresh looks like a new image and misses the cache. The source object accepts **`cacheKey`**: `source={{ uri: signedUrl, cacheKey: 'receipt-' + id }}` keeps one cache entry per receipt however often the URL changes. ## placeholder `placeholder` is shown **while the real image loads**, or when no source is set: 1. Another image: a small local asset or a low-resolution URL. 2. A **hash**: `{ blurhash: '...' }` or `{ thumbhash: '...' }`, a compact string the backend computes once per image and sends with the list data. The component renders a blurred preview from it without a network request. Two details avoid visual glitches: - **`placeholderContentFit`** defaults to **`'scale-down'`**, while `contentFit` defaults to **`'cover'`**; matching them prevents the placeholder and the final image from being framed differently. - **`transition`**, as a number of milliseconds, cross-fades from placeholder to image. ## Lists and recycling In virtualized lists, cells are **recycled**: a view that showed receipt A is reused for receipt B. Until B loads, the cell could still show A. **`recyclingKey`** fixes this: when it changes, the view resets to blank or the placeholder before loading the new image. Set it to the receipt id. ## How it compares with the core Image React Native's core `Image` has no `cachePolicy`, no `cacheKey` and no hash placeholders; its `defaultSource` shows only a static image while loading. Those are the reasons a list-heavy Expo app usually standardises on `expo-image`; the core component remains fine for simple static assets. ## Checklist for the receipt list - `source` with a stable `cacheKey`; - `cachePolicy="memory-disk"`; - `placeholder={{ blurhash }}` with `placeholderContentFit` matching `contentFit`; - `recyclingKey` set to the receipt id; - a short `transition`.
- Why can a list of receipt thumbnails re-download every image after each refresh even with caching on?Without a `cacheKey`, `expo-image` uses the `uri` as the key. Signed URLs change their query string when re-signed, so each refresh looks like a new image. Passing a stable `cacheKey`, such as the receipt id, in the source object keeps the cache hit.
- When would you choose cachePolicy 'none'?For images that should not persist on the device, for example sensitive documents after sign-out, or images that change constantly under the same URL. Every display then reloads the image, so it trades speed and data for not leaving cached copies.
cachePolicy is like choosing where to keep receipts you look at often: on your desk (memory), in a filing cabinet (disk), or both. The desk is fastest but gets cleared whenever space runs short, so for papers you keep coming back to, you want the cabinet as a backup.
saying these in an interview costs you the question
- expo-image does not cache unless cachePolicy is set.
- 'memory' is always the best cachePolicy because it is fastest.
- The placeholder uses the same contentFit as the image by default.
- Changing a signed URL's query string still hits the same cache entry.
- recyclingKey is only needed for images that fail to load.