Flexbox Layout

Web CSS gives you several layout systems to choose from — normal block flow, inline flow, floats, Grid, and Flexbox. React Native takes a much simpler approach: Flexbox is the only layout system there is. Every component you position on screen — a header, a card, a row of buttons — is arranged using the same small set of flexbox properties. There's no display: grid, no floats, no absolute-by-default block flow to fall back on. Learn flexbox well here, and you've learned essentially all of React Native layout.

The single most important difference from web CSS: in web CSS, a flex container's flexDirection defaults to 'row'. In React Native, it defaults to 'column'. This one difference trips up nearly every developer coming from web development — children stack top-to-bottom by default in React Native, not left-to-right.

flexDirection: 'column' vs 'row'

flexDirection controls the main axis — the direction children are laid out along. Since it defaults to 'column', this View's children stack vertically with no extra code:

// Default: flexDirection is 'column' — children stack top to bottom
<View style={styles.stack}>
  <Text>First</Text>
  <Text>Second</Text>
  <Text>Third</Text>
</View>

const styles = StyleSheet.create({
  stack: {
    flexDirection: 'column', // explicit here, but this is already the default
  },
});

To lay children out horizontally instead — the web's default behavior — you must explicitly set flexDirection: 'row':

<View style={styles.row}>
  <Text>First</Text>
  <Text>Second</Text>
  <Text>Third</Text>
</View>

const styles = StyleSheet.create({
  row: {
    flexDirection: 'row', // children now sit side by side
  },
});

justifyContent (Main Axis) and alignItems (Cross Axis)

Once you know the main axis (set by flexDirection), two more properties handle alignment:

PropertyWhat it aligns
justifyContentAligns children along the main axis (the direction set by flexDirection)
alignItemsAligns children along the cross axis (perpendicular to flexDirection)

Common values for both, and what they do:

ValueEffect
'flex-start'Packs children toward the start of the axis (default for alignItems and justifyContent)
'center'Centers children along that axis
'flex-end'Packs children toward the end of the axis
'space-between'justifyContent only — even space between children, none at the outer edges
'space-around'justifyContent only — even space around each child, including the outer edges
// Centers children both horizontally and vertically inside the View
const styles = StyleSheet.create({
  centered: {
    flex: 1,
    flexDirection: 'column',
    justifyContent: 'center', // main axis: vertical
    alignItems: 'center',     // cross axis: horizontal
  },
});

The flex Property: Growing to Fill Available Space

flex: 1 tells a component to grow and consume any remaining space along the main axis, after fixed-size siblings have taken what they need. This is the single most useful layout property in React Native — it's how you build screens that adapt to any device size without hardcoding pixel dimensions.

import React from 'react';
import { View, Text, StyleSheet } from 'react-native';

export default function SidebarLayout() {
  return (
    <View style={styles.row}>
      <View style={styles.sidebar}>
        <Text>Fixed 80px</Text>
      </View>
      <View style={styles.content}>
        <Text>Fills the rest (flex: 1)</Text>
      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  row: {
    flex: 1,
    flexDirection: 'row',
  },
  sidebar: {
    width: 80,          // fixed width — takes exactly 80px, no more, no less
    backgroundColor: '#333333',
  },
  content: {
    flex: 1,          // grows to absorb every remaining pixel of width
    backgroundColor: '#f0f0f0',
  },
});

Here, sidebar always stays exactly 80 pixels wide regardless of screen size, and content — the box with flex: 1 — automatically expands to fill whatever width is left over. Resize the device or rotate it, and the flexible box adjusts automatically; the fixed box never changes.

flexWrap: Letting Children Wrap Onto Multiple Lines

By default, flex children are laid out on a single line (row or column) and will shrink to fit rather than wrap. Setting flexWrap: 'wrap' allows children to flow onto additional lines once they run out of room on the main axis — handy for things like a grid of tags or thumbnail images:

<View style={styles.tagContainer}>
  <Text style={styles.tag}>React</Text>
  <Text style={styles.tag}>Native</Text>
  <Text style={styles.tag}>Mobile</Text>
  <Text style={styles.tag}>JavaScript</Text>
  <Text style={styles.tag}>Cross-Platform</Text>
</View>

const styles = StyleSheet.create({
  tagContainer: {
    flexDirection: 'row',
    flexWrap: 'wrap', // tags overflow onto a new line instead of shrinking
  },
  tag: {
    backgroundColor: '#e0ecff',
    paddingVertical: 6,
    paddingHorizontal: 10,
    borderRadius: 14,
    margin: 4,
  },
});

Full Worked Example: A Screen Layout (Header + Content + Footer)

Putting it all together — a very common real-world pattern: a fixed header, a flexible content area that takes whatever space is left, and a fixed footer, all stacked in a column:

import React from 'react';
import { View, Text, StyleSheet } from 'react-native';

export default function ScreenLayout() {
  return (
    <View style={styles.screen}>
      <View style={styles.header}>
        <Text style={styles.headerText}>My App</Text>
      </View>

      <View style={styles.content}>
        <Text>Main content area — grows to fill all leftover space</Text>
      </View>

      <View style={styles.footer}>
        <Text style={styles.footerText}>© 2026 My App</Text>
      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  screen: {
    flex: 1,               // fills the whole device screen
    flexDirection: 'column', // header, content, footer stack vertically
  },
  header: {
    height: 60,             // fixed height
    backgroundColor: '#2196f3',
    justifyContent: 'center',
    alignItems: 'center',
  },
  headerText: {
    color: '#ffffff',
    fontSize: 18,
    fontWeight: 'bold',
  },
  content: {
    flex: 1,               // absorbs all space left after header + footer
    padding: 16,
    justifyContent: 'center',
    alignItems: 'center',
  },
  footer: {
    height: 44,             // fixed height
    backgroundColor: '#eeeeee',
    justifyContent: 'center',
    alignItems: 'center',
  },
  footerText: {
    color: '#555555',
    fontSize: 12,
  },
});

Notice the pattern: only the content View gets flex: 1. The header and footer both use fixed height values instead, so they stay a constant size no matter what device the app runs on — exactly like the sidebar example earlier, just rotated into a column.

Next up: Now that components are styled and positioned, the next page covers how users actually interact with them — Touches & Gestures.