0Pricing
React Native Academy · Lesson

flex, flexGrow, and Responsive Sizing

Use the flex property to distribute available space proportionally between sibling Views, and build a layout that adjusts to any screen width.

flex, flexGrow, and Responsive Sizing is a free React Native Academy lesson on CoddyKit — lesson 3 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the React Native Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

The flex Property Explained

The flex property is a shorthand that sets how a View grows and shrinks to fill available space within its parent. In React Native, flex is similar to CSS's flex-grow — it represents the proportion of available space the component takes. Setting flex: 1 on a single child makes it fill all remaining space. When multiple siblings have flex values, the space is divided proportionally: a child with flex: 2 gets twice as much space as a sibling with flex: 1.

import { View } from 'react-native';

export default function FlexDemo() {
  return (
    <View style={{ flex: 1, flexDirection: 'column' }}>
      {/* Takes 1/3 of height */}
      <View style={{ flex: 1, backgroundColor: '#ff6b6b' }} />
      {/* Takes 2/3 of height */}
      <View style={{ flex: 2, backgroundColor: '#4f86f7' }} />
    </View>
  );
}

flex: 1 on the Root Container

If your root App component or the outermost screen View doesn't have flex: 1, the component collapses to the size of its content — it does not fill the screen. This is one of the most common React Native bugs for beginners: the app appears to have no layout because the root View has zero height. Always set flex: 1 on the root-level View and any intermediate containers that should fill available space. Child Views without a fixed size or flex property will shrink to wrap their content.

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

// WRONG: root View has no flex, collapses to text height
function WrongLayout() {
  return (
    <View style={{ backgroundColor: '#f5f5f5' }}>
      <Text>This View only wraps its content.</Text>
    </View>
  );
}

// CORRECT: flex: 1 fills the screen
function CorrectLayout() {
  return (
    <View style={{ flex: 1, backgroundColor: '#f5f5f5' }}>
      <Text>This View fills the entire screen.</Text>
    </View>
  );
}

flexGrow, flexShrink, and flexBasis

React Native's flex shorthand sets flexGrow, flexShrink, and flexBasis together. flexGrow controls how much a child grows into extra space. flexShrink controls how much it shrinks when space is tight. flexBasis sets the initial size before growing or shrinking (like CSS flex-basis). In practice, flex: 1 covers most use cases. Use flexShrink: 1 on a Text inside a row when you want it to shrink and show ellipsis instead of overflowing the row.

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

export default function FlexShrinkExample() {
  return (
    <View style={styles.row}>
      <View style={styles.icon} />
      {/* flexShrink: 1 allows text to shrink if the row is too small */}
      <Text style={styles.label} numberOfLines={1}>
        A very long label that could overflow without flexShrink
      </Text>
      <Text style={styles.badge}>NEW</Text>
    </View>
  );
}

const styles = StyleSheet.create({
  row: { flexDirection: 'row', alignItems: 'center', padding: 12, gap: 8 },
  icon: { width: 40, height: 40, borderRadius: 8, backgroundColor: '#4f86f7', flexShrink: 0 },
  label: { flex: 1, flexShrink: 1, fontSize: 16, color: '#333' },
  badge: { paddingHorizontal: 8, paddingVertical: 2, backgroundColor: '#ff6b6b', color: '#fff', borderRadius: 10, fontSize: 11, fontWeight: 'bold' },
});

Dimensions API for Screen Size

To get the current screen width and height in JavaScript, use the Dimensions API from react-native. Dimensions.get('window') returns the visible app area (excluding system bars), while Dimensions.get('screen') returns the full physical screen. However, Dimensions is a static snapshot — it won't update if the user rotates the device. For responsive layouts that react to orientation changes, use the useWindowDimensions hook, which returns updated values on every resize.

import { View, Text, Dimensions, useWindowDimensions } from 'react-native';

// Static (doesn't update on rotation)
const { width, height } = Dimensions.get('window');

// Reactive (updates on rotation — preferred)
export default function ResponsiveScreen() {
  const { width, height } = useWindowDimensions();
  const isLandscape = width > height;

  return (
    <View style={{ flex: 1, padding: 16 }}>
      <Text>Width: {Math.round(width)}</Text>
      <Text>Height: {Math.round(height)}</Text>
      <Text>Mode: {isLandscape ? 'Landscape' : 'Portrait'}</Text>
    </View>
  );
}

Percentage Widths for Responsive Columns

Use percentage strings for width to create layouts that adapt to different screen sizes without hardcoding pixel values. A two-column grid uses width: '48%' on each card with a small gap. A three-column layout uses width: '32%'. The percentage is relative to the parent container's width. To ensure the math works, set the parent's width explicitly or make it flex: 1, and use flexWrap: 'wrap' on the row container so columns flow to the next line correctly.

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

export default function TwoColumnGrid() {
  return (
    <View style={styles.grid}>
      {[1, 2, 3, 4, 5, 6].map(n => (
        <View key={n} style={styles.card} />
      ))}
    </View>
  );
}

const styles = StyleSheet.create({
  grid: {
    flexDirection: 'row',
    flexWrap: 'wrap',
    gap: 12,
    padding: 16,
  },
  card: {
    width: '47%',     // ~2 per row with gap
    height: 120,
    backgroundColor: '#4f86f7',
    borderRadius: 12,
  },
});

Combining flex with Fixed Dimensions

You can mix flex-based sizing with fixed width and height values in the same layout. A common pattern is a sidebar layout: a fixed-width side panel (e.g., width: 280) next to a content area with flex: 1 that fills the rest of the screen. The fixed-size panel takes exactly 280 points regardless of screen size, while the content panel grows or shrinks to use all remaining space. This also works for headers and footers: fixed height containers at top/bottom with a flex: 1 content area in between.

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

export default function MasterDetail() {
  return (
    <View style={styles.container}>
      {/* Fixed-width sidebar */}
      <View style={styles.sidebar}>
        <Text style={{ color: '#fff' }}>Sidebar</Text>
      </View>
      {/* flex: 1 content fills remaining space */}
      <View style={styles.content}>
        <Text>Main content area</Text>
      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, flexDirection: 'row' },
  sidebar: { width: 200, backgroundColor: '#333', padding: 16 },
  content: { flex: 1, backgroundColor: '#f5f5f5', padding: 16 },
});

useWindowDimensions for Adaptive Layouts

The useWindowDimensions hook re-renders your component whenever the device orientation changes or a multi-window layout resizes. Use it to dynamically switch between single-column (portrait) and two-column (landscape) layouts. Calculate the number of columns based on the window width: if width is above a threshold (e.g., 600), switch to two or three columns. This approach makes your app look good on phones, large phones, tablets, and even desktops with Expo Web — without writing separate layout components.

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

export default function AdaptiveGrid({ items }) {
  const { width } = useWindowDimensions();
  const numColumns = width >= 768 ? 3 : width >= 480 ? 2 : 1;
  const cardWidth = (width - 32 - (numColumns - 1) * 12) / numColumns;

  return (
    <View style={styles.grid}>
      {items.map(item => (
        <View key={item.id} style={[styles.card, { width: cardWidth }]} />
      ))}
    </View>
  );
}

const styles = StyleSheet.create({
  grid: { flexDirection: 'row', flexWrap: 'wrap', gap: 12, padding: 16 },
  card: { height: 120, backgroundColor: '#4f86f7', borderRadius: 10 },
});

minWidth, maxWidth, minHeight, maxHeight

Use minWidth, maxWidth, minHeight, and maxHeight to constrain how much a View can grow or shrink. These are useful for creating components that look good across a range of content sizes without breaking at extremes. For example, a button might have a minWidth: 120 so it is always touchable even for a single-character label, but a maxWidth: 320 so it never stretches across a full tablet screen. Combining these with flex: 1 and width: '100%' is the typical pattern for centered, bounded content on larger screens.

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

export default function BoundedContent() {
  return (
    <View style={styles.screen}>
      {/* Content is at most 480px wide, centered */}
      <View style={styles.content}>
        <Text style={styles.text}>This content is bounded to look good on all screen sizes.</Text>
      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  screen: { flex: 1, alignItems: 'center', backgroundColor: '#f5f5f5', padding: 16 },
  content: {
    width: '100%',
    maxWidth: 480, // doesn't stretch on tablets/web
    backgroundColor: '#fff',
    borderRadius: 12,
    padding: 20,
  },
  text: { fontSize: 16, lineHeight: 24, color: '#333' },
});

Aspect Ratio for Consistent Proportions

The aspectRatio style property sets the width-to-height ratio of a component. When the width is set (or determined by flex), aspectRatio automatically computes the height to maintain the proportion. aspectRatio: 16/9 creates a widescreen video thumbnail. aspectRatio: 1 creates a perfect square. This is especially powerful for media content that must adapt to different screen widths — the height automatically scales proportionally without any calculations or JavaScript.

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

export default function VideoThumbnail() {
  return (
    <View style={styles.thumbnailContainer}>
      <Image
        source={{ uri: 'https://picsum.photos/640/360' }}
        style={styles.thumbnail}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  thumbnailContainer: {
    width: '100%',
  },
  thumbnail: {
    width: '100%',
    aspectRatio: 16 / 9,  // height auto-computed = width * 9/16
    borderRadius: 10,
  },
});

Three-Panel Responsive Layout

Bring together all the sizing concepts: flex, maxWidth, and useWindowDimensions to build a classic three-panel layout for a note-taking or email app. On small phones, only one panel shows at a time. On tablets in landscape, all three panels appear side by side using flex ratios. This adaptive pattern is achievable with React Native's Flexbox and dimension hooks — no complex CSS media queries or separate layouts needed.

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

export default function ThreePanel() {
  const { width } = useWindowDimensions();
  const isTablet = width >= 768;

  if (!isTablet) {
    // Phone: single panel
    return <View style={{ flex: 1, backgroundColor: '#f5f5f5' }} />;
  }

  // Tablet: three panels
  return (
    <View style={styles.row}>
      <View style={[styles.panel, { flex: 1 }]} />
      <View style={[styles.panel, { flex: 2, backgroundColor: '#e8f0fe' }]} />
      <View style={[styles.panel, { flex: 2 }]} />
    </View>
  );
}

const styles = StyleSheet.create({
  row: { flex: 1, flexDirection: 'row' },
  panel: { backgroundColor: '#f5f5f5', borderRightWidth: 1, borderRightColor: '#ddd' },
});

Avoiding Layout Pitfalls

Common React Native layout pitfalls to watch for: missing flex: 1 on a container makes it collapse to zero height; percentage widths without a sized parent resolve to zero; fixed heights in deeply nested views cause clipping on small screens; minHeight instead of flex on scroll containers prevents proper sizing. Debug by temporarily adding backgroundColor tints to each View — you'll immediately see which boxes are sized correctly and which are collapsing. When a component is invisible, suspect a zero-height parent before looking at the component's own styles.

// Debugging checklist when a component is invisible:
// 1. Does its parent have flex: 1 or an explicit height?
// 2. Does the component itself have flex or height/width?
// 3. Are percentage dimensions resolving against a zero-width parent?
// 4. Is backgroundColor set (to verify the View is rendered but transparent)?

// Quick visibility check:
<View style={{ flex: 1, backgroundColor: 'rgba(255,0,0,0.1)' }}>
  {/* If this shows red tint, the container is sized correctly */}
  <YourComponent />
</View>

Quick Check

Test your understanding of React Native Mobile Development concepts from this lesson.

Lesson Recap

In this lesson you learned: flex: 1 fills remaining available space proportionally, useWindowDimensions provides reactive screen dimensions for orientation-aware layouts, and aspectRatio automatically computes height from width to maintain consistent proportions. Next up we explore building a responsive card grid with Flexbox wrapping.

Frequently asked questions

Is the “flex, flexGrow, and Responsive Sizing” lesson free?

Yes — the full text of “flex, flexGrow, and Responsive Sizing” is free to read here on the web, and the React Native Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the React Native Academy course, upgrade to CoddyKit PRO.

What will I learn in “flex, flexGrow, and Responsive Sizing”?

Use the flex property to distribute available space proportionally between sibling Views, and build a layout that adjusts to any screen width. You practise React Native Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start React Native Academy?

No prior experience is required. React Native Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 3 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “flex, flexGrow, and Responsive Sizing” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this React Native Academy lesson?

Yes. Every React Native Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. StyleSheet.create and Inline Styles
  2. Flexbox Direction, JustifyContent, AlignItems
  3. flex, flexGrow, and Responsive Sizing
  4. Building a Responsive Card Grid
← Back to React Native Academy