Since React Native 0.87 deprecated ImageBackground, how do you put a price label over a listing photo, and why can't Image hold children?
answer
- Image is a leaf, children throw
- ImageBackground was a View plus Image
- absoluteFill the Image, content above
- deprecated in 0.87, not yet in 0.86
- size the container, not the Image
basics
~20 sUse a View sized by your layout, render an Image inside it with StyleSheet.absoluteFill, then the price label as a later sibling so it draws on top. Image cannot contain children: it throws. ImageBackground did exactly this wrapping and is deprecated since 0.87.
solid answer
~40 s`Image` is a leaf component: passing children throws an error telling you to use absolute positioning. `ImageBackground` was a convenience wrapper, a `View` containing an `Image` stretched with `StyleSheet.absoluteFill`, and React Native 0.87 deprecates it in favour of writing that composition yourself. So the listing card becomes a `View` with the card's size, an `Image` with `absoluteFill` and `resizeMode="cover"`, and the price label rendered after the image so it sits on top. Size the container, because the image follows it. Writing it yourself also removes `ImageBackground`'s extra `imageStyle` prop, and keeps full control over the image's props. Expo SDK 57 apps run React Native 0.86, where `ImageBackground` is not deprecated yet, but the same composition works there and avoids a later migration.
code
tsx · 26 linesimport {Image, StyleSheet, Text, View} from 'react-native';
export function ListingCard({photo, price}: {photo: string; price: string}) {
return (
<View style={styles.card}>
<Image source={{uri: photo}} style={StyleSheet.absoluteFill} resizeMode="cover" />
{/* Rendered after the Image, so it draws on top. */}
<View style={styles.priceTag}>
<Text style={styles.price}>{price}</Text>
</View>
</View>
);
}
const styles = StyleSheet.create({
card: {width: '100%', aspectRatio: 4 / 3, overflow: 'hidden', borderRadius: 8},
priceTag: {
position: 'absolute',
left: 12,
bottom: 12,
paddingHorizontal: 8,
paddingVertical: 4,
backgroundColor: 'rgba(0,0,0,0.6)',
},
price: {color: 'white', fontWeight: 'bold'},
});go deeper
Recall that Image cannot have children and that content over a photo goes in a View with an absolutely positioned Image.
Explain what ImageBackground did internally, why it was deprecated in 0.87, and why the container must own the size.
Migrate ImageBackground uses across a codebase safely, keeping sizing, text contrast and touch handling intact on both platforms.
Plan deprecation clean-ups alongside upgrades, batching related component migrations so they do not become upgrade blockers later.
## Why Image cannot hold children React Native's `backgroundImage` view style draws only **linear and radial gradients**; it cannot load a photo from a URL. And `Image` is a **leaf**: it renders one native image view and nothing inside it. Passing children to `Image` **throws** an error that says `Image` cannot contain children and suggests `ImageBackground` or absolute positioning. ## What ImageBackground actually was `ImageBackground` accepted the same props as `Image` plus children. Internally it rendered: 1. a `View` with the style you passed; 2. an `Image` inside it, positioned with `StyleSheet.absoluteFill` and given the container's `width` and `height`, plus an optional `imageStyle`; 3. your children after the image, so they draw on top. It was always a thin convenience, and its own docs said the implementation was basic and to write a custom component when needed. **React Native 0.87 deprecates `ImageBackground`** and recommends a `View` with an absolutely positioned `Image` instead. It still works, but is due for removal in a future release. ## The replacement A real-estate listing card shows a photo with a price label in the corner and a "New" badge: 1. A **container `View`** owns the size: `width: '100%'` and `aspectRatio: 4 / 3`, plus `overflow: 'hidden'` and a border radius if the design wants rounded corners. 2. An **`Image`** with `StyleSheet.absoluteFill` fills the container; `resizeMode="cover"` crops the photo to the card. 3. The **overlay content**, the price and the badge, comes **after** the `Image` in the JSX. Later siblings draw on top, and an absolutely positioned label can sit in any corner. | Concern | ImageBackground | View + Image | |---|---|---| | Size | style on the wrapper | style on the container `View` | | Image styling | separate `imageStyle` prop | the `Image`'s own `style` | | Image ref | separate `imageRef` prop | a normal `ref` on the `Image` | | Status | deprecated since 0.87 | the recommended pattern | ## Details that matter - **Size the container, not the image.** With `absoluteFill`, the image takes whatever size the container has. A container with no height, in a column with no content, collapses to zero and the photo disappears. - **Keep text readable.** A scrim, a semi-transparent `View` behind the label or a `backgroundImage` linear gradient across the bottom of the card, keeps white text legible over bright photos. - **Touch handling.** The absolutely positioned image sits under the overlay; if the whole card is pressable, wrap the container in a `Pressable` rather than attaching handlers to the image. - **Remote photo sizing still applies.** The image fills the container, so the container must have a real size before the photo loads. ## Migrating an existing codebase 1. Search for `ImageBackground` imports; each one is a candidate. 2. Replace the wrapper with a container `View` carrying the old `style`. 3. Move `imageStyle` onto the inner `Image`, and replace `imageRef` with a plain `ref`. 4. Give the inner `Image` `StyleSheet.absoluteFill` and the same `source` and `resizeMode`. 5. Keep children after the `Image` so they still draw on top. 6. Check each screen on both platforms, especially rounded corners and overlays that relied on the wrapper's size. A small shared `PhotoWithOverlay` component makes this a one-time change instead of a pattern repeated on every screen. ## Version notes - React Native **0.87** marks `ImageBackground` deprecated. - Expo SDK 57 ships React Native **0.86**, where it is not yet deprecated. Migrating early costs nothing and keeps the code ready for the upgrade. - The same release deprecated other APIs too, such as Android's `DrawerLayoutAndroid`, so codebases often handle them together in one upgrade.
- In React Native, what happens if you put a Text inside an Image component?`Image` throws an error saying it cannot contain children and suggesting `ImageBackground` or absolute positioning. `Image` is a leaf that renders a single native image view. Content over a picture goes in a container `View` as a sibling rendered after an absolutely positioned `Image`.
- In React Native, why does a listing card built as a View with an absoluteFill Image sometimes show nothing?An `Image` with `StyleSheet.absoluteFill` takes its size from the container and contributes none of its own. If the container has no width or height from its style or its other children, it lays out at zero and the photo disappears. Give the container a real size, such as a width plus an `aspectRatio`.
saying these in an interview costs you the question
- Image accepts children and renders them on top of the picture.
- ImageBackground was removed from React Native 0.87.
- A View's backgroundImage style can display a listing photo from a URL.
- An absolutely positioned Image gives its container a size.
- Overlay content must come before the Image in JSX to appear on top.