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
Viewalready hasdisplay: flexandflexDirection: column— you rarely setdisplayyourself. flexDirectionsets the main axis;justifyContentaligns children along it,alignItemsaligns them across it.flex: 1makes 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, andcolumnGapfor spacing between children — no more margin hacks. - There is no CSS Grid and no
floatin 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 (internallyflexGrow: <n>,flexShrink: 1,flexBasis: 0).flex: 0— the component is sized by itswidth/heightand is inflexible.flex: -1— normally sized bywidth/height, but shrinks tominWidth/minHeightwhen 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: 1on the root view. Without it the container collapses to the size of its children and yourjustifyContent/alignItemsappear to do nothing. The root screen view almost always needsflex: 1. - Assuming
rowis the default. It iscolumn. A horizontal toolbar needs an explicitflexDirection: 'row'. - Trying to make
<Text>a flex container. Layout props belong onView. Put text inside aViewand lay out theView— nested<Text>uses text flow, not Flexbox. - Reaching for CSS Grid. There is none. Build grids from a wrapping
flexDirection: 'row'withflexWrap: 'wrap'andgap, or nest rows inside columns. - Hard-coding sizes everywhere. Prefer
flex, percentages, andgapso layouts adapt across devices. Use theaspectRatioprop (e.g.aspectRatio: 16 / 9) to size a box by ratio instead of fixing both dimensions. - Confusing the axes.
justifyContentalways follows the main axis (flexDirection);alignItemsis always the cross axis. FlipflexDirectionand 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.