Documentation Index
Fetch the complete documentation index at: https://resources.devweekends.com/llms.txt
Use this file to discover all available pages before exploring further.
Learning Objectives
By the end of this module, you’ll understand:
- Flexbox fundamentals and layout principles
- Flex direction and wrapping
- Justify content and align items
- Flex sizing (grow, shrink, basis)
- Absolute positioning
- Common layout patterns
Flexbox Fundamentals
Flexbox is the primary layout system in React Native. Unlike CSS on the web, all components in React Native use Flexbox by default — there is no display: block or display: inline. Everything is a flex container.
Think of Flexbox like packing items into a suitcase. You decide the direction items are placed (left-to-right or top-to-bottom), how leftover space is distributed, and what happens when items do not fit.
Key difference from web CSS: In React Native, the default flexDirection is column (vertical), not row (horizontal). This matches mobile conventions where screens are taller than they are wide and content naturally flows top-to-bottom.
import { View, StyleSheet } from 'react-native';
function FlexExample() {
return (
<View style={styles.container}>
<View style={[styles.box, { backgroundColor: 'red' }]} />
<View style={[styles.box, { backgroundColor: 'green' }]} />
<View style={[styles.box, { backgroundColor: 'blue' }]} />
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1, // Takes all available space
flexDirection: 'column', // Default: stacks vertically
},
box: {
width: 50,
height: 50,
},
});
Flex Direction
Controls the main axis — the direction children are laid out. Once you set the main axis, the cross axis is always perpendicular to it. Every alignment property in Flexbox refers to one of these two axes, so understanding this distinction is critical.
| Value | Description |
|---|
column | Vertical stack (default) |
row | Horizontal stack |
column-reverse | Vertical, reversed order |
row-reverse | Horizontal, reversed order |
// Horizontal layout
<View style={{ flexDirection: 'row' }}>
<Text>First</Text>
<Text>Second</Text>
<Text>Third</Text>
</View>
// Vertical layout (default)
<View style={{ flexDirection: 'column' }}>
<Text>Top</Text>
<Text>Middle</Text>
<Text>Bottom</Text>
</View>
Justify Content
Aligns children along the main axis:
// Center items horizontally in a row
<View style={{ flexDirection: 'row', justifyContent: 'center' }}>
<Text>Centered</Text>
</View>
// Space items evenly
<View style={{ flexDirection: 'row', justifyContent: 'space-between' }}>
<Text>Left</Text>
<Text>Center</Text>
<Text>Right</Text>
</View>
Justify Content Options:
flex-start (default) - Align to start
flex-end - Align to end
center - Center items
space-between - Even spacing, edges at container bounds
space-around - Even spacing including before first and after last
space-evenly - Equal spacing between all items including edges
Align Items
Aligns children along the cross axis:
// Stretch children to fill cross axis (default)
<View style={{ alignItems: 'stretch' }}>
<View style={{ height: 50 }} /> // Width fills container
</View>
// Center items vertically
<View style={{
flexDirection: 'row',
height: 200,
alignItems: 'center'
}}>
<Text>Vertically centered</Text>
</View>
Align Items Options:
stretch (default) - Stretch to fill
flex-start - Align to cross-start
flex-end - Align to cross-end
center - Center on cross axis
baseline - Align text baselines
Align Self
Override alignItems for individual children:
<View style={{ alignItems: 'flex-start' }}>
<Text>Aligned start</Text>
<Text style={{ alignSelf: 'flex-end' }}>Aligned end</Text>
<Text style={{ alignSelf: 'center' }}>Centered</Text>
</View>
Flex Sizing
Flex sizing controls how available space is distributed among children. Think of it like splitting a restaurant bill: flex values are each person’s share. A child with flex: 2 gets twice the space of a sibling with flex: 1.
flex
Defines how a component grows relative to siblings:
// Both children share space equally
<View style={{ flexDirection: 'row' }}>
<View style={{ flex: 1, backgroundColor: 'red' }} />
<View style={{ flex: 1, backgroundColor: 'blue' }} />
</View>
// First takes 2/3, second takes 1/3
<View style={{ flexDirection: 'row' }}>
<View style={{ flex: 2, backgroundColor: 'red' }} />
<View style={{ flex: 1, backgroundColor: 'blue' }} />
</View>
// First is fixed, second fills remaining space
<View style={{ flexDirection: 'row' }}>
<View style={{ width: 100, backgroundColor: 'red' }} />
<View style={{ flex: 1, backgroundColor: 'blue' }} />
</View>
flexGrow
Similar to flex, but the key difference is that flexGrow distributes extra space after each child’s base size is accounted for, while flex ignores base size entirely. In practice, use flex for simple proportional layouts and flexGrow when children have a meaningful minimum width/height:
<View style={{ flexDirection: 'row' }}>
<View style={{
flexGrow: 1,
width: 50, // Base size
backgroundColor: 'red'
}} />
<View style={{
flexGrow: 2,
width: 50, // Base size
backgroundColor: 'blue'
}} />
</View>
flexShrink
Controls how components shrink when space is limited:
<View style={{ flexDirection: 'row', width: 200 }}>
<View style={{
flexShrink: 0, // Don't shrink below width
width: 150,
backgroundColor: 'red'
}} />
<View style={{
flexShrink: 1, // Can shrink
width: 150,
backgroundColor: 'blue'
}} />
</View>
flexBasis
Sets the initial size before flexGrow/flexShrink:
<View style={{ flexDirection: 'row' }}>
<View style={{
flexBasis: 100, // Start at 100, can grow
flexGrow: 1,
backgroundColor: 'red'
}} />
<View style={{
flexBasis: '50%', // Percentage also works
flexGrow: 1,
backgroundColor: 'blue'
}} />
</View>
Flex Wrap
Controls wrapping of flex items:
// Items wrap to next line when overflow
<View style={{
flexDirection: 'row',
flexWrap: 'wrap',
}}>
{items.map(item => (
<View key={item.id} style={styles.item} />
))}
</View>
const styles = StyleSheet.create({
item: {
width: '50%', // Two items per row
padding: 10,
},
});
Flex Wrap Options:
nowrap (default) - Single line
wrap - Multi-line, top to bottom
wrap-reverse - Multi-line, bottom to top
Align Content
Aligns wrapped lines within container (only works with flexWrap):
<View style={{
flexDirection: 'row',
flexWrap: 'wrap',
alignContent: 'center', // Centers wrapped lines
}}>
{/* Multiple rows of items */}
</View>
Options: flex-start, flex-end, center, stretch, space-between, space-around
Positioning
React Native supports two positioning modes. Relative positioning is the default and rarely needs to be set explicitly. Absolute positioning removes an element from the normal layout flow — it no longer pushes other elements around, similar to pulling a sticky note off a stack and placing it anywhere on your desk.
Relative Positioning (Default)
Elements positioned according to normal flow, with optional offsets from their natural position:
<View style={{ position: 'relative', top: 10, left: 20 }}>
{/* Offset from original position */}
</View>
Absolute Positioning
Removes element from document flow:
// Parent must have relative/absolute position
<View style={{ position: 'relative', flex: 1 }}>
{/* Positioned relative to parent */}
<View style={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
backgroundColor: 'rgba(0,0,0,0.5)'
}}>
<Text>Overlay</Text>
</View>
</View>
Absolute Fill Shortcut
import { StyleSheet } from 'react-native';
// Using spread
<View style={{ ...StyleSheet.absoluteFill, backgroundColor: 'blue' }} />
// Or
<View style={[StyleSheet.absoluteFill, { backgroundColor: 'blue' }]} />
// Equivalent to:
<View style={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
}} />
Z-Index
Controls stacking order of positioned elements:
<View style={{ position: 'relative' }}>
<View style={{
position: 'absolute',
zIndex: 1,
backgroundColor: 'red'
}} />
<View style={{
position: 'absolute',
zIndex: 2, // On top
backgroundColor: 'blue'
}} />
</View>
Common Layout Patterns
Center Content
const styles = StyleSheet.create({
center: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
});
Row with Space Between
const styles = StyleSheet.create({
rowBetween: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
padding: 16,
},
});
function ScreenWithFooter() {
return (
<View style={{ flex: 1 }}>
<ScrollView style={{ flex: 1 }}>
{/* Scrollable content */}
</ScrollView>
<View style={{ padding: 16 }}>
{/* Footer content */}
<Button title="Submit" />
</View>
</View>
);
}
function SplitScreen() {
return (
<View style={{ flex: 1, flexDirection: 'row' }}>
<View style={{ width: 250, backgroundColor: '#f5f5f5' }}>
{/* Sidebar */}
</View>
<View style={{ flex: 1 }}>
{/* Main content */}
</View>
</View>
);
}
Grid Layout
function Grid({ data }) {
return (
<View style={{ flexDirection: 'row', flexWrap: 'wrap' }}>
{data.map(item => (
<View
key={item.id}
style={{ width: '33.33%', padding: 8 }}
>
<Card item={item} />
</View>
))}
</View>
);
}
Card with Overlay
function CardWithOverlay({ image, title, onPress }) {
return (
<TouchableOpacity style={{ position: 'relative' }}>
<Image source={image} style={{ width: '100%', height: 200 }} />
<View style={{
...StyleSheet.absoluteFill,
backgroundColor: 'rgba(0,0,0,0.3)',
justifyContent: 'flex-end',
padding: 16,
}}>
<Text style={{ color: 'white', fontSize: 18 }}>{title}</Text>
</View>
</TouchableOpacity>
);
}
Gap Property
Before gap, you had to use margins on individual children to space them apart, which always created an unwanted margin on the first or last item. gap solves this cleanly — it adds space between items only, never at the edges. If you have used CSS Grid’s gap, this is the same concept.
// Add space between items
<View style={{
flexDirection: 'row',
gap: 16, // 16px gap between items
}}>
<Button title="Cancel" />
<Button title="Confirm" />
</View>
// Row and column gaps
<View style={{
flexDirection: 'row',
flexWrap: 'wrap',
rowGap: 16, // Vertical gap
columnGap: 8, // Horizontal gap
}}>
{items.map(item => <Card key={item.id} />)}
</View>
Note: Gap property requires React Native 0.71+ or Expo SDK 48+
Layout Props Reference
Container Props
| Prop | Type | Description |
|---|
flex | number | Grow factor |
flexDirection | string | Layout direction |
flexWrap | string | Wrapping behavior |
justifyContent | string | Main axis alignment |
alignItems | string | Cross axis alignment |
alignContent | string | Wrapped lines alignment |
Item Props
| Prop | Type | Description |
|---|
alignSelf | string | Override container alignItems |
flexGrow | number | Growth factor |
flexShrink | number | Shrink factor |
flexBasis | number/string | Initial size |
Position Props
| Prop | Type | Description |
|---|
position | string | 'relative' or 'absolute' |
top | number | Offset from top |
bottom | number | Offset from bottom |
left | number | Offset from left |
right | number | Offset from right |
zIndex | number | Stacking order |
Best Practices
- Use
flex: 1 for components that should fill available space — this is the single most important Flexbox pattern in React Native. Forgetting it is the most common reason a screen appears blank.
- Avoid fixed dimensions when possible — use
flex and percentages. Fixed pixel values that look perfect on your test device will break on a different screen size.
- Center anything with
justifyContent: 'center' and alignItems: 'center' — this two-property combo works regardless of flexDirection and is the universal centering recipe.
- Understand the axes —
justifyContent controls the main axis (direction of flexDirection), alignItems controls the cross axis (perpendicular). This is the mental model that makes all of Flexbox click.
- Use
gap instead of margins for spacing between items — it is cleaner, avoids edge-margin bugs, and requires React Native 0.71+.
- Test on extreme screen sizes — Flexbox is responsive by default, but edge cases like the iPhone SE (320pt wide) and iPad (768pt+ wide) often reveal layout assumptions. Run your app on both during development.