skip to content

In React Native Web, what DOM does an Image render, and how do resizeMode and its accessibility label map onto it?

level: middleimportance: nice to knowfreq 18%

answer

  1. not a single img tag
  2. background-image on an inner div
  3. resizeMode becomes background-size
  4. an invisible img keeps the context menu
  5. alt comes from the aria label

basics

~20 s

React Native Web renders Image as a div holding a background-image layer sized by resizeMode, plus an invisible img whose alt comes from aria-label or accessibilityLabel, so the context menu and assistive technology still see an image.

solid answer

~40 s

`Image` in `react-native-web` is not a bare `<img>`. It renders an outer `View` (a `div` with `overflow: hidden`), an absolutely filled inner `div` that shows the picture as a CSS `background-image`, and a hidden `<img>` with `opacity: 0` stacked in the same box. `resizeMode` maps to `background-size`: `cover` (the default) and `contain` directly, `stretch` to `100% 100%`, `center` and `repeat` to `auto`. The hidden `img` gets `alt` from `aria-label` or `accessibilityLabel` (empty otherwise) and `draggable={false}` unless you set it, and it exists so right-click "save image" and similar browser features still work. `blurRadius` and `tintColor` become CSS and SVG filters, and children throw in development: use `ImageBackground`.

go deeper

for a junior

Recall that Image still takes source and resizeMode on the web, and that an accessible label is needed for meaningful images.

for a middle

Describe the rendered tree: outer div, background-image layer sized by resizeMode, and the invisible img that carries alt and the browser's image features.

for a senior

Anticipate the consequences for browser tests, CSS rules that target img, accessibility audits of empty alt, zero-size remote images, and when to use ImageBackground.

for a principal

Judge whether the web build's image needs (lazy loading, responsive sources, SEO) justify a web-specific image component rather than relying on the portable one.

## Not an img tag Web developers expect `<Image>` to become `<img>`. In **React Native Web** it renders a small tree instead: 1. An **outer `View`** (a `div`) with `overflow: hidden`, `flexBasis: 'auto'` and `zIndex: 0`, carrying your `style`, `aria-label`, `testID` and layout. 2. An **inner `div`**, absolutely filling the outer one, that shows the picture as a CSS **`background-image: url(...)`**. 3. A **hidden `<img>`**, also absolutely filling the box with `opacity: 0` and `zIndex: -1`, pointing at the same URL. 4. When `tintColor` is set, an inline **SVG filter** used to recolour the image. The background-image approach lets React Native's `resizeMode` values map onto CSS background sizing. The invisible `img` exists so the browser still treats the area as an image. ## resizeMode on the web | `resizeMode` | CSS on the inner div | |---|---| | `cover` (default) | `background-size: cover` | | `contain` | `background-size: contain` | | `stretch` | `background-size: 100% 100%` | | `center` | `background-size: auto` (scaled down if larger than the box) | | `repeat` | `background-repeat: repeat`, tile at natural size (scaled down if larger than the box) | | `none` | `background-size: auto`, positioned at the origin | Setting `resizeMode` inside `style` still works but warns in development that it is deprecated; pass it as a prop. ## Accessibility and the browser's image features - The hidden `img` gets **`alt`** from `aria-label`, falling back to `accessibilityLabel`, and to an empty string when neither is set. An empty `alt` tells assistive technology the image is decorative, so a meaningful picture **needs a label**. - The outer `div` also receives `aria-label`. - The hidden `img` is what the user right-clicks: **"save image as"** and **"copy image"** work because it is a real image element on top of the painted background. - `draggable` defaults to `false` on that `img`, so a drag does not start the browser's native image drag unless you opt in. ## Sizing and loading - The outer `View` takes `width` and `height` from `source` when the source object includes them, or from a static asset's registered dimensions. A **remote URI with neither source dimensions nor a style size has no intrinsic height and renders zero pixels tall**, just as on native. - Loading goes through an internal loader with states idle, loading and loaded. `defaultSource` is shown while the main source loads, and `onLoadStart`, `onLoad`, `onError` and `onLoadEnd` fire from it. - `Image.getSize`, `Image.prefetch` and `Image.queryCache` are implemented on the web. ## Effects and restrictions - **`blurRadius`** becomes a CSS `blur(...)` filter and **`tintColor`** an SVG colour filter on the displayed layer. - `shadow*` styles are neutralised on the `Image` itself; wrap it in a `View` for a shadow. - **Children are not allowed**: in development, `Image` throws "The <Image> component cannot contain children", pointing you to `ImageBackground` or absolute positioning. ## Comparing it with the native component On iOS and Android, `Image` is a native image view that decodes and scales the bitmap itself, and `resizeMode` is a property of that view. On the web there is no such view, so React Native Web composes the behaviour from standard browser features: - **Scaling** comes from CSS background sizing rather than from the image element. - **Loading state** comes from its own loader rather than from the view. - **Accessibility** comes from the hidden element's `alt` rather than from an accessibility label on a native view. The public props stay the same, which is the point of the library; only the mechanism underneath changes, and that mechanism is what leaks into tests, audits and CSS. ## Why this matters in practice - Browser tests that look for `img[src]` find the hidden element, but visual checks look at a background image. - A CSS rule or an extension that targets `img` elements affects the invisible layer, not what is painted. - Accessibility audits flag meaningful images without labels because the generated `alt` is empty. ```tsx import { Image } from 'react-native'; <Image source={{ uri: 'https://example.com/cat.jpg', width: 320, height: 200 }} resizeMode="contain" aria-label="A cat asleep on a keyboard" /> // => div[aria-label] > div(background-image, background-size: contain) + img[alt="A cat asleep on a keyboard"] ```

  • In React Native Web, why might an accessibility audit flag every Image in an app as decorative?
    Because the hidden `img` that React Native Web renders takes its `alt` from `aria-label` or `accessibilityLabel`, and falls back to an empty string. An empty `alt` marks an image as decorative. Meaningful images need a label prop; purely decorative ones can keep the empty default.
  • In React Native Web, what happens if you put a Text inside an Image to caption it?
    In development the `Image` throws, saying it cannot contain children and suggesting `ImageBackground` or absolute positioning. `ImageBackground` renders the image as an absolutely filled layer behind its children, which is the supported way to overlay content.

saying these in an interview costs you the question

  • React Native Web renders Image as a single img element with object-fit.
  • The alt attribute is generated from the image file name.
  • resizeMode has no effect on the web.
  • Right-click save does not work because the picture is a CSS background.
  • Placing children inside an Image works on the web.