skip to content

In React Native, what unit is a style value such as width: 120, and how does PixelRatio map it to physical pixels?

level: juniorimportance: must knowfreq 66%

answer

  1. no units in a style object
  2. density-independent points, dp on Android
  3. PixelRatio.get() is the scale
  4. pixels = points x scale
  5. getPixelSizeForLayoutSize rounds to integers

basics

~20 s

Style lengths are unitless density-independent points (dp on Android). PixelRatio.get() returns the device's scale, so width: 120 covers 120 x scale physical pixels: 360 on a 3x screen. getPixelSizeForLayoutSize does that conversion, rounded to an integer.

solid answer

~40 s

React Native style numbers have no unit: they are **density-independent points** (iOS points, Android dp), so `width: 120` is roughly the same physical size on every phone. The device's density is `PixelRatio.get()`, which is the `scale` of the window: 1 on low-density Android screens, 1.5 or 2 on mid-density ones, 3 on many current phones. Physical pixels are points times that scale, so `width: 120` spans 360 pixels at 3x. `PixelRatio.getPixelSizeForLayoutSize(120)` performs the conversion and rounds to a whole number, which is what you use when asking a server for an image of the right resolution. There is no `px`, `rem` or viewport unit; percentages are the only other length form.

code

tsx · 13 lines
tsx
import { Image, PixelRatio } from 'react-native';

const THUMB = 120;

export function RecipeThumb({ baseUrl }: { baseUrl: string }) {
  const px = PixelRatio.getPixelSizeForLayoutSize(THUMB);
  return (
    <Image
      source={{ uri: `${baseUrl}?w=${px}&h=${px}` }}
      style={{ width: THUMB, height: THUMB }}
    />
  );
}

go deeper

for a junior

Recall that style numbers are unitless points, that PixelRatio.get() is the density, and that pixels equal points times that ratio.

for a middle

Explain what getPixelSizeForLayoutSize and roundToNearestPixel return, and why layout stays in points while image requests use pixels.

for a senior

Show where density matters in production: remote image sizing, memory in long lists, and why density must never drive layout breakpoints.

for a principal

Set a policy for image delivery across densities, trading bandwidth, memory and sharpness, and decide what the image service must support.

## Points, not pixels Every length in a React Native style, `width`, `height`, `margin`, `borderWidth`, `fontSize`, is a plain number with no unit. That number is a **density-independent point**: iOS calls it a point, Android calls it a dp. The screen's physical pixels are a separate grid underneath. The point exists so that a layout keeps roughly the same physical size across devices. A recipe thumbnail with `width: 120` is about the same size on a small low-density Android phone and on a high-density flagship, even though the second one packs far more pixels into it. The mapping from points to physical units is not exact across all devices, but the difference is rarely noticeable. ## PixelRatio.get and the scale factor `PixelRatio.get()` returns the device's **pixel density**: how many physical pixels one point spans along each axis. In the source it is simply the `scale` field of `Dimensions.get('window')`. | `PixelRatio.get()` | Typical devices | |---|---| | `1` | Low-density (mdpi) Android screens | | `1.5` | hdpi Android screens | | `2` | xhdpi Android screens, many older iPhones | | `3` | xxhdpi Android screens, iPhones such as the X and 11 Pro | | `3.5` | Some xxxhdpi Android screens | So the conversion is always: **physical pixels = points x `PixelRatio.get()`**. At 3x, `width: 120` spans 360 physical pixels. ## One thumbnail across densities The same 120-point recipe thumbnail covers very different pixel counts: | `PixelRatio.get()` | Physical pixels for `width: 120` | Image to request | |---|---|---| | `1` | 120 | 120 x 120 | | `1.5` | 180 | 180 x 180 | | `2` | 240 | 240 x 240 | | `3` | 360 | 360 x 360 | The layout is identical on all four: the thumbnail takes the same share of the screen. Only the number of pixels available to draw it changes, which is why density matters for image sharpness and memory, but not for layout decisions. ## Converting sizes in code `PixelRatio` offers two helpers built on that multiplication: 1. `PixelRatio.getPixelSizeForLayoutSize(points)` returns `Math.round(points * PixelRatio.get())`, so it is **guaranteed to be an integer** number of pixels. At 3x, `getPixelSizeForLayoutSize(33.5)` is 101. 2. `PixelRatio.roundToNearestPixel(points)` goes the other way: it returns the nearest point value that lands on a whole number of pixels, for example 8.33 instead of 8.4 at 3x. ## Requesting a correctly sized remote image The most common use of `getPixelSizeForLayoutSize` is fetching remote images at the right resolution. A recipe list shows 120-point thumbnails. Requesting a 120-pixel image would look soft on a 3x phone, because it is stretched across 360 pixels; requesting a 1,200-pixel image wastes bandwidth and memory. Multiply the layout size by the ratio and ask the image service for that: ```tsx const px = PixelRatio.getPixelSizeForLayoutSize(120); const uri = `${baseUrl}?w=${px}&h=${px}`; ``` The `Image` still gets `width: 120, height: 120` in its style, because layout is always in points. Only the **source request** is in pixels. ## Units that do not exist - `'120px'`, `'8rem'`, `'10em'`, `'30vw'` and `'50vh'` are not valid lengths; styles take numbers or percentage strings such as `'50%'`. - There is no automatic "1 CSS pixel equals 1 point" translation: you are always working in points. - `fontSize` is also in points, and the user's text-size setting multiplies it further; that font scale is reported separately. ## Common mistakes - **Multiplying layout sizes by the ratio**: `width: 120 * PixelRatio.get()` makes the view three times too big at 3x. Layout stays in points; only pixel-level requests use pixels. - **Assuming one density per platform**: Android devices ship at many densities, and iOS devices at 2x and 3x. - **Using the ratio for breakpoints**: density says nothing about how much room the app has; a large tablet can have a lower ratio than a small phone. Breakpoints come from the window's width in points. - **Caching the ratio forever**: the value comes from the window metrics, so read it when you need it instead of freezing it at import time. Knowing that the style number is a point and that `PixelRatio.get()` is the bridge to pixels answers most "why is this image blurry" and "why is this view the wrong size" questions.

  • Why not simply request every remote thumbnail at 3x to be safe?
    A 3x image on a 1.5x or 2x device is downscaled after download, so you pay for bandwidth and decoded image memory you never show. In a long recipe list that adds up quickly. Requesting `getPixelSizeForLayoutSize(size)` fetches exactly what the screen can display.
  • Is PixelRatio.get() the same value as useWindowDimensions().scale?
    Yes. `PixelRatio.get()` reads `Dimensions.get('window').scale`, which is the same `scale` field that `useWindowDimensions()` returns. The hook's advantage is that it re-renders the component if the window metrics change; `PixelRatio.get()` just returns the current value when called.

saying these in an interview costs you the question

  • React Native style numbers are physical pixels.
  • You can write width: '120px' or '8rem' in a style.
  • Multiply every layout size by PixelRatio.get() to support high-density screens.
  • All Android devices share a single pixel ratio.
  • A higher pixel ratio means the device has more room for layout.