Section 2 · Layout and Style
Flexbox with Mobile Defaults
20 min3 min readFill in the blanks
Lesson at a glance
Lay out Trailhead’s list rows and screens with flexbox, knowing the mobile defaults (column direction, no shrinking, stretch) that explain most squashed or overflowing layouts.
- 1Same flexbox, different defaults
- 2flex: 1, the property you’ll type most
- 3Building the hike row
- 4Absolute positioning and wrapping
No video here — just the real thing: a written lesson, runnable examples, a quiz and practice.
Here’s a layout bug every web developer hits in their first week of React Native: a row with an icon, a title and a chevron looks perfect with “Ridge Loop” and breaks with “Old Mill Creek Trail to Eagle Point Lookout”. The chevron vanishes off the right edge. Nothing is wrong with your flexbox knowledge; React Native’s flexbox simply starts from different defaults.
Same flexbox, different defaults
React Native lays out every View with flexbox (via Yoga, a layout engine that implements the spec). There’s no display: block or inline; every View is a flex container. Four defaults differ from the web:
| Property | Web default | React Native default |
|---|---|---|
flexDirection |
row |
column |
flexShrink |
1 |
0 |
alignContent |
normal |
flex-start |
flex |
a shorthand | a single number |
The first two explain most surprises. Children stack vertically unless you say flexDirection: 'row', and they don’t shrink when space runs out, which is why that long title pushed the chevron away.
The rules you know still hold: justifyContent works along the main axis, alignItems across it. In a column (the default), justifyContent: 'center' centers vertically and alignItems: 'center' centers horizontally. Switch to a row and the two swap.
alignItems defaults to stretch, so a child in a column fills the full width unless you give it a width or change alignItems. That’s why a Text with a background color in a plain column stretches edge to edge.
flex: 1, the property you’ll type most
In React Native, flex takes a single number. flex: 1 means “grow to fill the remaining space along the main axis, and you may shrink”. Two places need it almost always:
- The root View of every screen, so it fills the screen instead of hugging its content.
- Every parent of a ScrollView or FlatList, because a scroll container scrolls within the height it’s given. If no ancestor bounds that height, it grows to its full content and the bottom gets cut off without ever scrolling.
When a screen renders as a thin strip at the top, or a list won’t scroll, walk up the tree and look for the missing flex: 1.
Building the hike row
Here’s the row that broke, fixed:
import { Image, StyleSheet, Text, View } from 'react-native';
export function HikeRow({ hike }: { hike: { name: string; distanceKm: number; date: string; thumbUrl: string } }) {
return (
<View style={styles.row}>
<Image source={{ uri: hike.thumbUrl }} style={styles.thumb} />
<View style={styles.body}>
<Text style={styles.title} numberOfLines={1}>{hike.name}</Text>
<Text style={styles.meta}>{hike.distanceKm} km · {hike.date}</Text>
</View>
<Text style={styles.chevron}>›</Text>
</View>
);
}
const styles = StyleSheet.create({
row: { flexDirection: 'row', alignItems: 'center', gap: 12, padding: 12 },
thumb: { width: 56, height: 56, borderRadius: 8 },
body: { flex: 1 },
title: { fontSize: 17, fontWeight: '600' },
meta: { fontSize: 14, color: '#5c6b62', marginTop: 2 },
chevron: { fontSize: 24, color: '#9aa79f' },
});Read it axis by axis. The row’s main axis is horizontal; alignItems: 'center' centers the three children vertically against the tallest one (the thumbnail). The thumbnail and chevron have fixed sizes. The body has flex: 1, so it takes whatever width is left, and numberOfLines={1} truncates a long name with an ellipsis instead of pushing the chevron off screen.
Two more tools round out everyday layout. alignSelf lets one child break from its parent’s alignItems: a “Log a hike” button in a stretched column can use alignSelf: 'flex-end' to sit on the right at its natural width. And aspectRatio sizes a box from one dimension, which is ideal for photos: give the trail photo width: '100%' and aspectRatio: 4 / 3, and its height follows the screen width on every phone without you calculating anything.
When a layout misbehaves, the fastest debugging trick is still the oldest one: give the suspect Views a temporary bright background color. You’ll see at once which box is too small, which one stretched, and which one never got its flex: 1.
Absolute positioning and wrapping
Every View is position: 'relative' by default, so position: 'absolute' places a child relative to its parent. That’s how you put a “New” badge on the corner of a thumbnail:
<View>
<Image source={{ uri: hike.thumbUrl }} style={styles.thumb} />
<View style={{ position: 'absolute', top: -4, right: -4, backgroundColor: '#e8590c', borderRadius: 8, paddingHorizontal: 6 }}>
<Text style={{ color: 'white', fontSize: 11, fontWeight: '700' }}>NEW</Text>
</View>
</View>For a row of tag chips (“steep”, “lake”, “dog-friendly”) that should flow onto a second line when they run out of room, combine flexDirection: 'row', flexWrap: 'wrap' and gap: 8. Absolute positioning takes the child out of the flex flow entirely, so use it for overlays only, never to fake a layout that flex can express.
Next you’ll make these layouts hold up across phone sizes, notches, rounded corners and the two platforms’ differences.
Key takeaways
- Every View is a flex container, and the main axis runs top to bottom by default (
flexDirection: 'column'). justifyContentpositions children along the main axis;alignItemspositions them across it and defaults tostretch.- Children don’t shrink by default in React Native, so a long title pushes its siblings off screen until you give it
flex: 1. flex: 1means take the remaining space; a screen’s root View and every parent of a ScrollView need it.- Use
position: 'absolute'for overlays like badges, andflexWrap: 'wrap'withgapfor chip rows.