skip to content

Styles & Flexbox

Styling is JavaScript objects laid out by Yoga's flexbox in density-independent units, with no cascade and no media queries. Interviewers ask how it differs from CSS, where web developers trip first.

part ofReact Nativeoverview, primer and where to startread it →
on this pageshow

explore

questions

26

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.
open as a page

In a bare React Native project, what must you do so a bundled .ttf file can be used through the Text fontFamily style?

level: juniorimportance: must knowfreq 55%

basics

~10 s

Copy the font into each native project: android/app/src/main/assets/fonts on Android, and the iOS app target plus a UIAppFonts entry in Info.plist. Then rebuild the native app and reference it by name in fontFamily.

open as a page

In React Native, how do you apply a conditional style such as a disabled coupon card, and which style wins when entries conflict?

level: juniorimportance: must knowfreq 68%

basics

~20 s

React Native's style prop accepts an array, so a disabled card uses [styles.card, disabled && styles.cardDisabled]. Falsy entries are skipped, and for each property the last entry that sets it wins; there are no selectors or cascade.

open as a page

What does StyleSheet.create do in React Native 0.87, and how does it differ from passing an inline style object?

level: juniorimportance: must knowfreq 64%

basics

~20 s

In React Native 0.87, StyleSheet.create returns the object it is given, freezing each style in development; its practical benefit is TypeScript checking. An inline style object works the same but is recreated on every render.

open as a page

In React Native, how does useColorScheme report the system light or dark setting, and why prefer it over Appearance.getColorScheme()?

level: juniorimportance: must knowfreq 68%

basics

~10 s

useColorScheme returns 'light' or 'dark' (null only without the native Appearance module) and re-renders the component whenever the scheme changes. Appearance.getColorScheme() returns the same value once, so a stored result goes stale.

open as a page

In React Native, which Yoga flexbox defaults differ from CSS, and why does a two-column pricing layout ported from the web stack vertically?

level: juniorimportance: must knowfreq 78%

basics

~20 s

Yoga defaults flexDirection to column, flexShrink to 0, alignContent to flex-start and position to relative, and takes flex as a single number. A ported row of plan cards therefore stacks vertically until its container sets flexDirection: 'row'.

open as a page

In React Native, why does a layout sized with Dimensions.get at module scope stay stuck at launch size, and how does useWindowDimensions fix it?

level: middleimportance: must knowfreq 62%

basics

~20 s

Dimensions.get returns a snapshot, and a StyleSheet built at module scope captures it once, so rotation, unfolding or split-screen never update it. useWindowDimensions subscribes to Dimensions change events and re-renders the component with the new window size.

open as a page

In React Native, why can one fontFamily string find a custom font on iOS but not on Android, and how do you fix the naming?

level: middleimportance: must knowfreq 48%

basics

~20 s

iOS resolves fontFamily against names stored inside the font, its family or PostScript name, while Android's asset lookup matches the file name. Renaming each file to its PostScript name, or a per-platform mapping, makes one string work.

open as a page

In a React Native app, how do you theme colors through a context-provided palette while keeping layout in a static StyleSheet?

level: middleimportance: must knowfreq 58%

basics

~10 s

Keep layout (padding, flex, radii, fonts) in a module-scope StyleSheet.create, select a light or dark palette object from useColorScheme in one provider, share it through context, and apply colours per render in style arrays.

open as a page

In React Native, why does a long Text beside an icon in a flexDirection row run past the screen edge instead of wrapping?

level: middleimportance: must knowfreq 62%

basics

~20 s

Yoga's default flexShrink is 0, so the Text keeps the width it measured for its content even when the row is too narrow, and overflows. Setting flexShrink: 1 (or flex: 1) on the Text lets it narrow and wrap.

open as a page

In React Native, how does the single-number flex style behave for positive, zero and negative values, and when does flex: 1 fill nothing?

level: juniorimportance: should knowfreq 52%

basics

~20 s

React Native's flex is one number: a positive value grows in proportion from a zero basis, 0 keeps the view at its width and height, and -1 keeps that size but may shrink to its minimums. flex: 1 fills nothing under an unsized parent.

open as a page

In React Native, when do you still need PixelRatio.roundToNearestPixel if the framework already snaps layout to the pixel grid?

level: middleimportance: should knowfreq 24%

basics

~20 s

React Native rounds layout positions and sizes to whole pixels when it applies them to native views. roundToNearestPixel is for values you compute and apply outside that path, such as offsets or transforms, so they land on whole pixels and line up.

open as a page

In an Expo project, when would you embed a custom font with the expo-font config plugin instead of loading it with useFonts?

level: middleimportance: should knowfreq 42%

basics

~20 s

Embed with the expo-font config plugin for native builds: the font ships in the binary and is usable on the first frame. Use useFonts for Expo Go or web, accepting an async load and a render gate.

open as a page

In React Native, what do StyleSheet.flatten and StyleSheet.compose return, and when would you use each instead of a plain style array?

level: middleimportance: should knowfreq 34%

basics

~20 s

StyleSheet.flatten resolves a style or nested array into one object where later entries win, returning a lone object unchanged. StyleSheet.compose(a, b) returns [a, b], or just the other style when one is null or undefined.

open as a page

In React Native, what does Appearance.setColorScheme override, and how does a banking app return to following the system on 0.87 versus 0.86?

level: middleimportance: should knowfreq 36%

basics

~20 s

Appearance.setColorScheme('light' | 'dark') forces the app, including its native alerts and pickers, into one scheme without touching the system setting. On 0.87 'auto' removes the override; on 0.86 (Expo SDK 57) the reset value is 'unspecified'.

open as a page

In React Native, where views default to position relative, how does an absolutely positioned badge find its anchor, and what does position: 'static' change?

level: middleimportance: should knowfreq 40%

basics

~20 s

An absolute view's insets are measured from its containing block: the nearest ancestor that is not position: 'static' or has a transform. Views default to relative, so that is usually the parent; 'static' ignores insets and lets absolute children skip past it.

open as a page

A React Native recipe app should show one pane when a foldable phone is folded and two panes when unfolded; how would you build that switch?

level: seniorimportance: should knowfreq 34%

basics

~20 s

Treat the fold as a window resize: read width from useWindowDimensions at the screen root, pick one or two panes at a width threshold in points, and keep the selected recipe in state above both layouts so switching panes does not lose it.

open as a page

In a React Native fashion store app bundling a brand typeface in four weights, why do headings styled fontWeight '700' render in the system font on Android but correctly on iOS?

level: seniorimportance: should knowfreq 38%

basics

~20 s

iOS picks the nearest weight among the family's registered faces. Android's asset lookup only knows regular, _bold, _italic and _bold_italic files named after fontFamily, so fontWeight '700' on 'BrandSans-Regular' finds no file and falls back to the system font.

open as a page

You are building a reusable React Native CouponCard with a disabled variant; how should it type, order and merge a caller's style prop, given there are no selectors?

level: seniorimportance: should knowfreq 30%

basics

~20 s

Type the prop as StyleProp<ViewStyle> from react-native, merge it as the last array entry after the base and disabled styles, expose extra props for inner elements since selectors do not exist, and never spread a style prop into an object.

open as a page

A React Native banking app keeps light colors after the phone switches to dark mode with the app open; what are the likely causes and fixes?

level: seniorimportance: should knowfreq 32%

basics

~20 s

Either the reported scheme is wrong, from native config restricting the app to light or a leftover setColorScheme override, or it is right but not read reactively, from colours captured at module scope, in initial state or outside context.

open as a page

A React Native pricing grid built from fixed widths and margins breaks on tablets; how would you rebuild it with flexWrap, gap, percentages and aspectRatio?

level: seniorimportance: should knowfreq 30%

basics

~20 s

Make the container a wrapping row with gap, give each card a preferred flexBasis plus flexGrow: 1 (and a maxWidth) so the number per line follows the width, and size artwork with width: '100%' and aspectRatio instead of fixed heights.

open as a page

In React Native, what do PixelRatio.getFontScale() and useWindowDimensions().fontScale report, and how should layout respond when the user enlarges text?

level: middleimportance: nice to knowfreq 16%

basics

~20 s

Both report the user's system text-size multiplier, around 1 at the default size and higher when text is enlarged. useWindowDimensions re-renders when it changes, so layout can reflow, for example stacking a two-column ingredient row, instead of clipping.

open as a page

In React Native 0.87, what do StyleSheet.absoluteFill and StyleSheet.hairlineWidth provide, and what replaced StyleSheet.absoluteFillObject?

level: middleimportance: nice to knowfreq 22%

basics

~20 s

StyleSheet.absoluteFill is a ready-made style that pins a view to all four edges of its parent; absoluteFillObject was removed in 0.85 in its favour. StyleSheet.hairlineWidth is the platform's thin-line width, rounded to whole physical pixels.

open as a page

In React Native, what does PlatformColor return, and when would a banking app use it instead of its own hex palette?

level: middleimportance: nice to knowfreq 20%

basics

~20 s

PlatformColor('name', ...fallbacks) returns an opaque reference to a native system colour, resolved on the native side. It suits UI that should match the platform, such as label and background colours, and it adapts to theme and contrast settings.

open as a page

Since React Native 0.77, what do boxSizing: 'content-box' and display: 'contents' change about how Yoga lays out a View?

level: middleimportance: nice to knowfreq 12%

basics

~20 s

boxSizing: 'content-box' makes width and height apply to the content box, so padding and border are added outside; the default remains border-box. display: 'contents' removes a View's own layout box so its children lay out as direct children of its parent.

open as a page

In React Native, why can text in a custom font with the same fontSize and lineHeight sit at different heights on Android and iOS?

level: seniorimportance: nice to knowfreq 22%

basics

~20 s

Each platform builds the line box from the font's own ascent and descent differently. Android adds font padding by default (includeFontPadding: true) and splits lineHeight's extra space above and below; iOS pins the line height and shifts the baseline.

open as a page