Skip to main content

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.
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 primary axis of layout:
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

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

Same as flex, but respects base size:
<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

Relative Positioning (Default)

Elements positioned according to normal document flow:
<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

Modern way to space flex items:
// 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
  2. Avoid fixed dimensions when possible - use flex and percentages
  3. Combine justifyContent + alignItems for centering
  4. Understand the axes: main axis = flexDirection, cross axis = perpendicular
  5. Use gap property instead of margins for spacing (when available)
  6. Test on different screen sizes - flexbox is responsive by default }