Flexbox Layout
Web CSS gives you several layout systems to choose from — normal block flow, inline flow,
floats, Grid, and Flexbox. React Native takes a much simpler approach: Flexbox is the
only layout system there is. Every component you position on screen — a header, a card,
a row of buttons — is arranged using the same small set of flexbox properties. There's no
display: grid, no floats, no absolute-by-default block flow to fall back on. Learn
flexbox well here, and you've learned essentially all of React Native layout.
flexDirection defaults to 'row'. In React Native, it defaults to 'column'. This one difference trips up nearly every developer coming from web development — children stack top-to-bottom by default in React Native, not left-to-right.flexDirection: 'column' vs 'row'
flexDirection controls the main axis — the direction children are
laid out along. Since it defaults to 'column', this View's children stack vertically
with no extra code:
// Default: flexDirection is 'column' — children stack top to bottom
<View style={styles.stack}>
<Text>First</Text>
<Text>Second</Text>
<Text>Third</Text>
</View>
const styles = StyleSheet.create({
stack: {
flexDirection: 'column', // explicit here, but this is already the default
},
});
To lay children out horizontally instead — the web's default behavior — you must explicitly set
flexDirection: 'row':
<View style={styles.row}>
<Text>First</Text>
<Text>Second</Text>
<Text>Third</Text>
</View>
const styles = StyleSheet.create({
row: {
flexDirection: 'row', // children now sit side by side
},
});
justifyContent (Main Axis) and alignItems (Cross Axis)
Once you know the main axis (set by flexDirection), two more properties handle
alignment:
| Property | What it aligns |
|---|---|
justifyContent | Aligns children along the main axis (the direction set by flexDirection) |
alignItems | Aligns children along the cross axis (perpendicular to flexDirection) |
Common values for both, and what they do:
| Value | Effect |
|---|---|
'flex-start' | Packs children toward the start of the axis (default for alignItems and justifyContent) |
'center' | Centers children along that axis |
'flex-end' | Packs children toward the end of the axis |
'space-between' | justifyContent only — even space between children, none at the outer edges |
'space-around' | justifyContent only — even space around each child, including the outer edges |
// Centers children both horizontally and vertically inside the View
const styles = StyleSheet.create({
centered: {
flex: 1,
flexDirection: 'column',
justifyContent: 'center', // main axis: vertical
alignItems: 'center', // cross axis: horizontal
},
});
The flex Property: Growing to Fill Available Space
flex: 1 tells a component to grow and consume any remaining space along the main
axis, after fixed-size siblings have taken what they need. This is the single most useful
layout property in React Native — it's how you build screens that adapt to any device size
without hardcoding pixel dimensions.
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
export default function SidebarLayout() {
return (
<View style={styles.row}>
<View style={styles.sidebar}>
<Text>Fixed 80px</Text>
</View>
<View style={styles.content}>
<Text>Fills the rest (flex: 1)</Text>
</View>
</View>
);
}
const styles = StyleSheet.create({
row: {
flex: 1,
flexDirection: 'row',
},
sidebar: {
width: 80, // fixed width — takes exactly 80px, no more, no less
backgroundColor: '#333333',
},
content: {
flex: 1, // grows to absorb every remaining pixel of width
backgroundColor: '#f0f0f0',
},
});
Here, sidebar always stays exactly 80 pixels wide regardless of screen size, and
content — the box with flex: 1 — automatically expands to fill
whatever width is left over. Resize the device or rotate it, and the flexible box adjusts
automatically; the fixed box never changes.
flexWrap: Letting Children Wrap Onto Multiple Lines
By default, flex children are laid out on a single line (row or column) and will shrink to fit
rather than wrap. Setting flexWrap: 'wrap' allows children to flow onto additional
lines once they run out of room on the main axis — handy for things like a grid of tags or
thumbnail images:
<View style={styles.tagContainer}>
<Text style={styles.tag}>React</Text>
<Text style={styles.tag}>Native</Text>
<Text style={styles.tag}>Mobile</Text>
<Text style={styles.tag}>JavaScript</Text>
<Text style={styles.tag}>Cross-Platform</Text>
</View>
const styles = StyleSheet.create({
tagContainer: {
flexDirection: 'row',
flexWrap: 'wrap', // tags overflow onto a new line instead of shrinking
},
tag: {
backgroundColor: '#e0ecff',
paddingVertical: 6,
paddingHorizontal: 10,
borderRadius: 14,
margin: 4,
},
});
Full Worked Example: A Screen Layout (Header + Content + Footer)
Putting it all together — a very common real-world pattern: a fixed header, a flexible content area that takes whatever space is left, and a fixed footer, all stacked in a column:
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
export default function ScreenLayout() {
return (
<View style={styles.screen}>
<View style={styles.header}>
<Text style={styles.headerText}>My App</Text>
</View>
<View style={styles.content}>
<Text>Main content area — grows to fill all leftover space</Text>
</View>
<View style={styles.footer}>
<Text style={styles.footerText}>© 2026 My App</Text>
</View>
</View>
);
}
const styles = StyleSheet.create({
screen: {
flex: 1, // fills the whole device screen
flexDirection: 'column', // header, content, footer stack vertically
},
header: {
height: 60, // fixed height
backgroundColor: '#2196f3',
justifyContent: 'center',
alignItems: 'center',
},
headerText: {
color: '#ffffff',
fontSize: 18,
fontWeight: 'bold',
},
content: {
flex: 1, // absorbs all space left after header + footer
padding: 16,
justifyContent: 'center',
alignItems: 'center',
},
footer: {
height: 44, // fixed height
backgroundColor: '#eeeeee',
justifyContent: 'center',
alignItems: 'center',
},
footerText: {
color: '#555555',
fontSize: 12,
},
});
Notice the pattern: only the content View gets flex: 1. The header
and footer both use fixed height values instead, so they stay a constant size no
matter what device the app runs on — exactly like the sidebar example earlier, just rotated
into a column.