Flexbox in React Native: The Complete Layout Guide

Blog / React Native · April 14, 2020 · Updated June 10, 2026 · 10 min read
Flexbox in React Native: The Complete Layout Guide

In React Native, Flexbox is the primary layout system: every <View> is a flex container by default, and you position children by setting properties such as flexDirection, justifyContent, and alignItems inside a StyleSheet. Two things trip up web developers immediately — flexDirection defaults to column (top to bottom) instead of row, and flex is a single number instead of the grow shrink basis shorthand. Under the hood React Native runs your styles through Meta's Yoga layout engine, a cross-platform implementation of most of the CSS Flexbox spec that deliberately leaves out CSS Grid and floats.

Key takeaways

  • Every View already has display: flex and flexDirection: column — you rarely set display yourself.
  • flexDirection sets the main axis; justifyContent aligns children along it, alignItems aligns them across it.
  • flex: 1 makes a component expand to fill available space — forgetting it on the root view is the most common layout bug.
  • Numbers are density-independent pixels (dp); use a string like '50%' for percentages.
  • Modern React Native (0.71+) supports gap, rowGap, and columnGap for spacing between children — no more margin hacks.
  • There is no CSS Grid and no float in React Native; you compose grids from nested flex rows and columns.

How layout works: the Yoga engine

React Native does not run in a browser, so there is no CSS file and no DOM box model. Instead, the JavaScript style objects you pass to a component are handed to Yoga, a layout engine maintained by Meta. Yoga computes the position and size of every node and feeds the result to the native iOS and Android views. That is why layout feels familiar to anyone who knows CSS Flexbox while differing in a few details.

Styles are plain JavaScript objects. The idiomatic way to define them is StyleSheet.create, which validates the keys and lets React Native reference each style by id:

import { StyleSheet, View, Text } from 'react-native';

/** A simple centered card built entirely with Flexbox. */
export default function Card() {
  return (
    <View style={styles.container}>
      <Text style={styles.title}>Hello, Flexbox</Text>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,                  // fill the whole screen
    justifyContent: 'center', // center on the main (vertical) axis
    alignItems: 'center',     // center on the cross (horizontal) axis
    backgroundColor: '#fff',
  },
  title: { fontSize: 20, fontWeight: '600' },
});

React Native Flexbox vs CSS Flexbox

If you come from the web, the API looks identical but the defaults and a few rules differ. Keep this table handy:

Aspect React Native CSS Flexbox (web)
Default flexDirection column (top to bottom) row (left to right)
Who is a flex container every View by default only elements with display: flex
flex property a single number (e.g. flex: 1) shorthand for flex-grow flex-shrink flex-basis
Units unitless dp numbers (padding: 16) require units (16px, 1rem)
Percentages supported as strings (width: '50%') supported (50%)
gap / rowGap / columnGap supported as numbers, RN 0.71+ supported as lengths
CSS Grid not available available
float / clear not available available
position values relative (default), absolute, static (0.72+) static, relative, absolute, fixed, sticky
display values flex (default), none block, flex, grid, inline, …

The single most important line: flexDirection defaults to column. A horizontal row of items on the web becomes a vertical stack in React Native unless you explicitly set flexDirection: 'row'.

The core layout props

flexDirection — choose the main axis

flexDirection decides the direction children flow, i.e. the main axis. It accepts 'column' (the default), 'row', 'column-reverse', and 'row-reverse'. Set it to 'row' to lay children out horizontally:

import { StyleSheet, View } from 'react-native';

/** Four boxes laid out left to right. */
export default function Row() {
  return (
    <View style={styles.row}>
      <View style={[styles.box, { backgroundColor: '#ef4444' }]} />
      <View style={[styles.box, { backgroundColor: '#3b82f6' }]} />
      <View style={[styles.box, { backgroundColor: '#22c55e' }]} />
      <View style={[styles.box, { backgroundColor: '#111827' }]} />
    </View>
  );
}

const styles = StyleSheet.create({
  row: { flexDirection: 'row' },
  box: { width: 50, height: 50 },
});

justifyContent — align along the main axis

justifyContent distributes children along the main axis (horizontal when flexDirection is row, vertical when it is column). Values: 'flex-start' (default), 'flex-end', 'center', 'space-between', 'space-around', and 'space-evenly'.

import { StyleSheet, View } from 'react-native';

export default function SpacedRow() {
  return (
    <View style={styles.row}>
      <View style={styles.box} />
      <View style={styles.box} />
      <View style={styles.box} />
    </View>
  );
}

const styles = StyleSheet.create({
  row: {
    flexDirection: 'row',
    justifyContent: 'space-between', // even gaps, edges flush
    padding: 16,
  },
  box: { width: 50, height: 50, backgroundColor: '#6366f1' },
});

alignItems — align across the cross axis

alignItems controls alignment on the cross axis (perpendicular to flexDirection). Values: 'stretch' (default), 'flex-start', 'flex-end', 'center', and 'baseline'. Because the default is stretch, children with no fixed cross-axis size expand to fill the container.

alignSelf — override alignment for one child

alignSelf lets a single child opt out of its parent's alignItems. It takes the same values plus 'auto'.

alignContent — align wrapped lines

When children wrap onto multiple lines (flexWrap: 'wrap'), alignContent controls how those lines pack on the cross axis. It is ignored for single-line layouts.

import { StyleSheet, View } from 'react-native';

/** Center children vertically; the green box overrides itself to the bottom. */
export default function Aligned() {
  return (
    <View style={styles.row}>
      <View style={styles.box} />
      <View style={[styles.box, styles.selfEnd]} />
      <View style={styles.box} />
    </View>
  );
}

const styles = StyleSheet.create({
  row: {
    flex: 1,
    flexDirection: 'row',
    alignItems: 'center', // cross axis = vertical here
  },
  box: { width: 50, height: 50, backgroundColor: '#0ea5e9' },
  selfEnd: { alignSelf: 'flex-end', backgroundColor: '#22c55e' },
});

flex, flexGrow, flexShrink, flexBasis

In React Native flex is a single number, and it behaves like this:

  • flex: <positive> — the component is flexible and sized proportionally to its flex value (internally flexGrow: <n>, flexShrink: 1, flexBasis: 0).
  • flex: 0 — the component is sized by its width/height and is inflexible.
  • flex: -1 — normally sized by width/height, but shrinks to minWidth/minHeight when space is tight.

For finer control you can still set flexGrow, flexShrink, and flexBasis individually. To split a container 20% / 30% / 50%, give children proportional flex values:

import { StyleSheet, View } from 'react-native';

/** Three stacked rows taking 20% / 30% / 50% of the height. */
export default function Proportions() {
  return (
    <View style={styles.fill}>
      <View style={{ flex: 2, backgroundColor: '#ef4444' }} />
      <View style={{ flex: 3, backgroundColor: '#22c55e' }} />
      <View style={{ flex: 5, backgroundColor: '#3b82f6' }} />
    </View>
  );
}

const styles = StyleSheet.create({
  fill: { flex: 1 }, // flexDirection defaults to 'column'
});

flexWrap and gap

By default children stay on a single line (flexWrap: 'nowrap') and may overflow. Set flexWrap: 'wrap' to let them flow onto new lines. To add consistent spacing between children, modern React Native (0.71+) supports gap, rowGap, and columnGap as plain numbers — far cleaner than per-child margins:

import { StyleSheet, View } from 'react-native';

const COLORS = ['#ef4444', '#f59e0b', '#22c55e', '#3b82f6', '#8b5cf6', '#ec4899'];

/** A wrapping tag/grid layout using gap instead of margins. */
export default function WrapGrid() {
  return (
    <View style={styles.wrap}>
      {COLORS.map((c) => (
        <View key={c} style={[styles.chip, { backgroundColor: c }]} />
      ))}
    </View>
  );
}

const styles = StyleSheet.create({
  wrap: {
    flexDirection: 'row',
    flexWrap: 'wrap',
    gap: 12, // space between both rows and columns
    padding: 16,
  },
  chip: { width: 80, height: 40, borderRadius: 8 },
});

Practical layouts you will actually build

Perfectly center anything

The classic "center a box in the middle of the screen" needs just three lines on the root view:

import { StyleSheet, View, Text } from 'react-native';

export default function Centered() {
  return (
    <View style={styles.screen}>
      <View style={styles.badge}>
        <Text style={styles.text}>Centered</Text>
      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  screen: { flex: 1, justifyContent: 'center', alignItems: 'center' },
  badge: { padding: 24, borderRadius: 12, backgroundColor: '#111827' },
  text: { color: '#fff', fontWeight: '600' },
});

Equal-width columns

Give each column flex: 1 and they share the row equally regardless of screen size. Add gap for spacing:

import { StyleSheet, View } from 'react-native';

export default function Columns() {
  return (
    <View style={styles.row}>
      <View style={[styles.col, { backgroundColor: '#ef4444' }]} />
      <View style={[styles.col, { backgroundColor: '#22c55e' }]} />
      <View style={[styles.col, { backgroundColor: '#3b82f6' }]} />
    </View>
  );
}

const styles = StyleSheet.create({
  row: { flexDirection: 'row', gap: 8, height: 120 },
  col: { flex: 1, borderRadius: 8 },
});

Header, content, and footer

A full-screen app shell is a column where the body takes flex: 1 and the header and footer keep their natural height:

import { StyleSheet, View, Text } from 'react-native';

export default function AppShell() {
  return (
    <View style={styles.screen}>
      <View style={styles.header}><Text>Header</Text></View>
      <View style={styles.body}><Text>Content fills the gap</Text></View>
      <View style={styles.footer}><Text>Footer</Text></View>
    </View>
  );
}

const styles = StyleSheet.create({
  screen: { flex: 1 },                 // column by default
  header: { height: 56, justifyContent: 'center', paddingHorizontal: 16 },
  body: { flex: 1, padding: 16 },      // expands to fill remaining space
  footer: { height: 56, justifyContent: 'center', paddingHorizontal: 16 },
});

Responsive layouts and the safe area

Phones, foldables, and tablets vary widely, so read the live dimensions with the useWindowDimensions hook (it re-renders on rotation) and switch the axis or use percentages. For notches and the home indicator, wrap screens in SafeAreaView from react-native-safe-area-context.

import { StyleSheet, View, useWindowDimensions } from 'react-native';

/** Stack on narrow screens, go side-by-side on wide ones. */
export default function Responsive() {
  const { width } = useWindowDimensions();
  const isWide = width >= 600;
  return (
    <View style={[styles.container, { flexDirection: isWide ? 'row' : 'column' }]}>
      <View style={[styles.pane, { backgroundColor: '#6366f1' }]} />
      <View style={[styles.pane, { backgroundColor: '#22c55e' }]} />
    </View>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, gap: 12, padding: 12 },
  pane: { flex: 1, borderRadius: 8 },
});

Common gotchas (and how to fix them)

  • Forgetting flex: 1 on the root view. Without it the container collapses to the size of its children and your justifyContent/alignItems appear to do nothing. The root screen view almost always needs flex: 1.
  • Assuming row is the default. It is column. A horizontal toolbar needs an explicit flexDirection: 'row'.
  • Trying to make <Text> a flex container. Layout props belong on View. Put text inside a View and lay out the View — nested <Text> uses text flow, not Flexbox.
  • Reaching for CSS Grid. There is none. Build grids from a wrapping flexDirection: 'row' with flexWrap: 'wrap' and gap, or nest rows inside columns.
  • Hard-coding sizes everywhere. Prefer flex, percentages, and gap so layouts adapt across devices. Use the aspectRatio prop (e.g. aspectRatio: 16 / 9) to size a box by ratio instead of fixing both dimensions.
  • Confusing the axes. justifyContent always follows the main axis (flexDirection); alignItems is always the cross axis. Flip flexDirection and their effects swap.

Flexbox is the foundation, but a polished cross-platform app also needs navigation, native modules, and platform conventions. For more React Native topics, see our guides on tracking location in a React Native Android app and famous apps built with React Native. Choosing a stack? Compare options in the best cross-platform framework for mobile app development and React Native vs Flutter.

If you would rather have an expert team build or rescue your app, MicroPyramid offers hands-on mobile app development services.

Frequently Asked Questions

Why does flexDirection default to column in React Native instead of row?

React Native's Yoga engine defaults flexDirection to column because mobile screens are taller than they are wide, so stacking content vertically is the most common case. This differs from CSS on the web, where the default is row. For a horizontal layout, set flexDirection: 'row' explicitly.

How is the flex property different from CSS flex?

In React Native flex is a single number, not the flex-grow flex-shrink flex-basis shorthand used on the web. A positive value such as flex: 1 makes the component grow to fill available space proportionally to its value; flex: 0 sizes it by its width/height; and flex: -1 lets it shrink below its base size when space is tight. For precise control you can set flexGrow, flexShrink, and flexBasis separately.

Does React Native support gap, rowGap, and columnGap?

Yes. Since React Native 0.71 (released January 2023) you can use gap, rowGap, and columnGap as plain numbers to add consistent spacing between children, including between wrapped rows. This replaces the older pattern of adding margins to each child.

Can I use CSS Grid in React Native?

No. React Native has no CSS Grid and no floats. You build grid-like layouts with Flexbox: a container with flexDirection: 'row', flexWrap: 'wrap', and a gap, or by nesting rows inside columns. The aspectRatio prop helps keep grid cells proportional.

How do I center a component both horizontally and vertically?

Set flex: 1, justifyContent: 'center', and alignItems: 'center' on the parent View. justifyContent centers along the main axis and alignItems along the cross axis, so the child lands in the middle regardless of screen size.

How do I make a React Native layout responsive?

Use the useWindowDimensions hook to read the current width and height (it updates on rotation and screen changes), then switch flexDirection, toggle styles, or use percentage strings such as width: '50%'. Combine this with flex values and gap instead of fixed pixel sizes, and wrap screens in SafeAreaView to respect notches.

Share this article