Lesson 6 of 26
Flexbox Layout in React Native
React Native lays out every screen with Flexbox and nothing else, with one classic gotcha — its default flexDirection is column, the opposite of the web's default.
If you've styled a page with Flexbox before, most of what follows will
feel familiar — flex, justifyContent, alignItems all mean the same
thing they do in CSS. But React Native makes one different default choice
that catches almost everyone the first time, and it's worth getting
straight before you build your first real screen.
Flexbox is the only layout system
Where CSS on the web gives you Flexbox, Grid, floats, and plain block/inline
flow to choose between, React Native gives you exactly one: Flexbox. Every
View is a flex container by default, and there is no Grid equivalent —
anything you'd reach for CSS Grid to build has to be expressed as nested
Flexbox containers instead. This is a genuine constraint, not a missing
feature waiting to be added; native layout on mobile has leaned on Flexbox
as the standard cross-platform model for a long time, and it comfortably
covers the layouts real apps need.
The gotcha: flexDirection defaults to column
import { View, Text, StyleSheet } from "react-native";
// Default flexDirection: children stack vertically
function StackedByDefault() {
return (
<View style={styles.container}>
<Text>First</Text>
<Text>Second</Text>
<Text>Third</Text>
</View>
);
}
// Explicit row: children now sit side by side
function ExplicitRow() {
return (
<View style={[styles.container, { flexDirection: "row" }]}>
<Text>First</Text>
<Text>Second</Text>
<Text>Third</Text>
</View>
);
}
const styles = StyleSheet.create({
container: { padding: 12 },
});If you're used to CSS and reach for a horizontal row of items, you'll get
a vertical stack instead unless you remember to add flexDirection: "row".
It's worth just memorizing this one, since it comes up on essentially
every screen with any horizontal layout — a header bar, a row of buttons,
an avatar next to a name.
flex, justifyContent, and alignItems
Once flexDirection is set correctly, the rest of Flexbox behaves the way
you already expect from CSS:
flexon a child controls how it grows to fill remaining space along the container's main axis.flex: 1on every child of a row splits the row evenly between them; giving one childflex: 2gives it twice the share of any other child withflex: 1.justifyContentaligns children along the main axis (horizontal for a row, vertical for a column) —"flex-start","center","flex-end","space-between","space-around".alignItemsaligns children along the cross axis (perpendicular to the main axis) —"flex-start","center","flex-end","stretch".
function Toolbar() {
return (
<View
style={{
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
padding: 12,
}}
>
<Text>Back</Text>
<Text>Title</Text>
<Text>Edit</Text>
</View>
);
}Here, flexDirection: "row" lays the three Text elements side by side,
justifyContent: "space-between" spreads them across the full width, and
alignItems: "center" keeps them vertically centered relative to each
other — a standard header-bar layout in three lines.
flexWrap for content that doesn't fit on one line
By default, flex children in a row try to fit on a single line, shrinking
if needed. flexWrap: "wrap" lets children flow onto additional lines
instead, which is the pattern behind things like a grid of tags or thumbnail
images:
function TagList({ tags }) {
return (
<View style={{ flexDirection: "row", flexWrap: "wrap", gap: 8 }}>
{tags.map((tag) => (
<View key={tag} style={{ padding: 6, backgroundColor: "#eee", borderRadius: 4 }}>
<Text>{tag}</Text>
</View>
))}
</View>
);
}A complete example
Putting the column default, an explicit row, and space distribution together into one realistic screen fragment:
function ProductCard({ name, price, imageUrl }) {
return (
// Outer View: default column — image on top, details below
<View style={{ borderRadius: 8, overflow: "hidden", backgroundColor: "#fff" }}>
<Image source={{ uri: imageUrl }} style={{ width: "100%", height: 160 }} />
<View style={{ padding: 12 }}>
{/* Inner row: name and price share the width */}
<View style={{ flexDirection: "row", justifyContent: "space-between" }}>
<Text style={{ fontWeight: "600" }}>{name}</Text>
<Text>{price}</Text>
</View>
</View>
</View>
);
}The outer View needs no explicit flexDirection at all, because
stacking the image above the details is exactly what the column default
already does. Only the inner row, where name and price sit side by side,
needs flexDirection: "row" spelled out.
What to remember
- React Native lays out every screen with Flexbox, and only Flexbox — there's no CSS Grid equivalent.
- The default flexDirection is column, not row — the opposite of the web's default, and the most common early layout surprise.
- flex distributes remaining space among children; justifyContent aligns along the main axis; alignItems aligns along the cross axis.
- flexWrap: "wrap" lets row children flow onto multiple lines instead of shrinking to fit one.
- When your layout is naturally vertical (like a card with an image above details), you often don't need to set flexDirection at all — the column default already does it.
Check yourself
3 questions · pass 3/3 to unlock StyleSheet and Styling Patterns
1.What is React Native's default flexDirection for a View, and how does it compare to the web's CSS default?
2.What does flex: 1 do when applied to a child inside a flex container?
3.Which CSS layout system does React Native support for laying out View trees?
3 left to answer