Skip to main content

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.
ValueDescription
columnVertical stack (default)
rowHorizontal stack
column-reverseVertical, reversed order
row-reverseHorizontal, 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

PropTypeDescription
flexnumberGrow factor
flexDirectionstringLayout direction
flexWrapstringWrapping behavior
justifyContentstringMain axis alignment
alignItemsstringCross axis alignment
alignContentstringWrapped lines alignment

Item Props

PropTypeDescription
alignSelfstringOverride container alignItems
flexGrownumberGrowth factor
flexShrinknumberShrink factor
flexBasisnumber/stringInitial size

Position Props

PropTypeDescription
positionstring'relative' or 'absolute'
topnumberOffset from top
bottomnumberOffset from bottom
leftnumberOffset from left
rightnumberOffset from right
zIndexnumberStacking order

Best Practices

  1. 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.
  2. 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.
  3. Center anything with justifyContent: 'center' and alignItems: 'center' — this two-property combo works regardless of flexDirection and is the universal centering recipe.
  4. Understand the axesjustifyContent controls the main axis (direction of flexDirection), alignItems controls the cross axis (perpendicular). This is the mental model that makes all of Flexbox click.
  5. Use gap instead of margins for spacing between items — it is cleaner, avoids edge-margin bugs, and requires React Native 0.71+.
  6. 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.