Skip to main content

🛡️ SafeAreaView: Respecting Device Boundaries

Ensuring your content avoids notches, status bars, and home indicators

🎯 Learning Objectives

By the end of this lesson, you will be able to:

  • Understand what safe areas are and why they matter
  • Use React Native's built-in SafeAreaView component
  • Work with react-native-safe-area-context for more control
  • Apply safe area insets selectively to specific edges
  • Handle safe areas in different screen contexts (modals, tabs)
  • Create layouts that work across all device types

⏱️ Estimated Time: 20-30 minutes

📑 In This Lesson

What Are Safe Areas?

Modern mobile devices have various hardware elements that intrude into the screen area: notches, Dynamic Island, rounded corners, status bars, and home indicators. The safe area is the portion of the screen where your content won't be obscured by these elements.

📖 Key Concept

Safe areas define the region of the screen that's guaranteed to be fully visible and interactive, avoiding hardware obstructions and system UI elements.

Device Safe Areas iPhone (Notch) Safe Area Top Inset (~47px) Bottom Inset (~34px) iPhone (Dynamic Island) Safe Area Top Inset (~59px) Android (Status Bar) 9:41 📶 🔋 Safe Area Top Inset (~24px) Nav bar may add bottom inset

Different devices have different safe area insets based on their hardware

The Problem Without Safe Areas

// ❌ Content hidden behind notch and home indicator
export default function App() {
  return (
    <View style={{ flex: 1 }}>
      <Text style={{ fontSize: 24 }}>Welcome!</Text>
      {/* This text might be hidden behind the notch! */}
    </View>
  );
}

Why This Matters

📱 Device Variety

iPhones have notches or Dynamic Island. Some Android phones have camera punch-holes. Insets vary by device.

🔄 Orientation Changes

Safe areas change when rotating between portrait and landscape modes.

🎯 Touch Targets

Buttons near edges might be unreachable if they're under the home indicator area.

👁️ Content Visibility

Important text or images shouldn't be obscured by hardware elements.

Basic SafeAreaView

React Native includes a built-in SafeAreaView component that automatically adds padding to avoid device boundaries.

Simple Usage

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

export default function App() {
  return (
    <SafeAreaView style={styles.container}>
      <Text style={styles.title}>Welcome!</Text>
      <Text>This content respects the safe area.</Text>
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
  },
  title: {
    fontSize: 24,
    fontWeight: 'bold',
  },
});

⚠️ Limitations of Built-in SafeAreaView

  • iOS only — On Android, it behaves like a regular View
  • All-or-nothing — Can't selectively apply insets to specific edges
  • No access to inset values — Can't use the numbers for calculations

For most apps, you'll want to use react-native-safe-area-context instead.

Typical App Structure

// Basic structure with SafeAreaView at root
import { SafeAreaView, ScrollView, View, Text } from 'react-native';

export default function HomeScreen() {
  return (
    <SafeAreaView style={{ flex: 1, backgroundColor: '#f5f5f5' }}>
      {/* Header */}
      <View style={styles.header}>
        <Text style={styles.headerTitle}>My App</Text>
      </View>
      
      {/* Scrollable Content */}
      <ScrollView contentContainerStyle={styles.content}>
        {/* Your content here */}
      </ScrollView>
      
      {/* Footer/Tab Bar would go here */}
    </SafeAreaView>
  );
}

react-native-safe-area-context

The react-native-safe-area-context library is the recommended way to handle safe areas. It works on both iOS and Android, provides access to inset values, and offers more control.

Installation

# With Expo
npx expo install react-native-safe-area-context

# With bare React Native
npm install react-native-safe-area-context
# Then run pod install for iOS

Setup with SafeAreaProvider

Wrap your app with SafeAreaProvider at the root:

// App.tsx
import { SafeAreaProvider } from 'react-native-safe-area-context';

export default function App() {
  return (
    <SafeAreaProvider>
      <Navigation />
    </SafeAreaProvider>
  );
}

✅ Expo Projects

If you're using Expo Router, the SafeAreaProvider is already set up for you! You can use the safe area hooks and components directly.

SafeAreaView from the Library

import { SafeAreaView } from 'react-native-safe-area-context';

function HomeScreen() {
  return (
    <SafeAreaView style={{ flex: 1 }}>
      {/* Content is automatically padded on all edges */}
      <Text>Safe content</Text>
    </SafeAreaView>
  );
}

Using the useSafeAreaInsets Hook

For more control, use the useSafeAreaInsets hook to get the actual inset values:

import { View, Text, StyleSheet } from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';

function CustomHeader() {
  const insets = useSafeAreaInsets();
  
  return (
    <View style={[
      styles.header,
      { paddingTop: insets.top + 10 }  // Safe area + extra padding
    ]}>
      <Text style={styles.title}>My App</Text>
    </View>
  );
}

const styles = StyleSheet.create({
  header: {
    backgroundColor: '#2196F3',
    paddingHorizontal: 16,
    paddingBottom: 10,
  },
  title: {
    color: 'white',
    fontSize: 20,
    fontWeight: 'bold',
  },
});

Inset Values

The useSafeAreaInsets hook returns an object with four values:

const insets = useSafeAreaInsets();

// insets = {
//   top: 47,     // Status bar / notch
//   right: 0,    // Usually 0 in portrait
//   bottom: 34,  // Home indicator
//   left: 0,     // Usually 0 in portrait
// }

// In landscape, left/right might have values for notch
insets.top insets.bottom insets.left insets.right Safe Area

The four inset values define padding needed on each edge

Selective Insets

Often you don't want safe area padding on all edges. For example, a screen with a tab bar already handles the bottom inset, so you only need the top.

Using the edges Prop

import { SafeAreaView } from 'react-native-safe-area-context';

// Only apply safe area to top
<SafeAreaView edges={['top']} style={{ flex: 1 }}>
  {/* Content */}
</SafeAreaView>

// Apply to top and bottom only
<SafeAreaView edges={['top', 'bottom']} style={{ flex: 1 }}>
  {/* Content */}
</SafeAreaView>

// All edges except bottom (for screens with tab bar)
<SafeAreaView edges={['top', 'left', 'right']} style={{ flex: 1 }}>
  {/* Content */}
</SafeAreaView>

Common Edge Configurations

Screen Type Edges Reason
Root screen (no nav) ['top', 'bottom', 'left', 'right'] Need all insets
Screen with header ['bottom'] or [] Header handles top
Screen with tab bar ['top'] Tab bar handles bottom
Screen with both [] Navigation handles both
Modal (full screen) ['top', 'bottom'] Usually portrait only

Manual Inset Application

Using the hook gives you full control over which insets to apply and how:

import { View, StyleSheet } from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';

function ScreenWithTabBar() {
  const insets = useSafeAreaInsets();

  return (
    <View style={[
      styles.container,
      { 
        // Only apply top inset - tab bar handles bottom
        paddingTop: insets.top,
        // In landscape, apply left/right for notch
        paddingLeft: insets.left,
        paddingRight: insets.right,
      }
    ]}>
      {/* Content */}
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
  },
});

Insets in ScrollView

For ScrollView, you can apply insets to the content area instead of the container:

import { ScrollView } from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';

function ScrollableScreen() {
  const insets = useSafeAreaInsets();

  return (
    <ScrollView
      style={{ flex: 1 }}
      contentContainerStyle={{
        paddingTop: insets.top,
        paddingBottom: insets.bottom + 20,  // Extra padding at bottom
        paddingHorizontal: 16,
      }}
    >
      {/* Scrollable content */}
    </ScrollView>
  );
}

Common Patterns

Let's look at real-world patterns for handling safe areas in different scenarios.

Full-Screen Background with Safe Content

When you want a background color or image to extend edge-to-edge, but content stays in the safe area:

import { View, StyleSheet } from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';

function FullScreenWithSafeContent() {
  const insets = useSafeAreaInsets();

  return (
    <View style={styles.container}>
      {/* Background extends to edges */}
      
      <View style={[
        styles.content,
        {
          paddingTop: insets.top,
          paddingBottom: insets.bottom,
          paddingLeft: insets.left,
          paddingRight: insets.right,
        }
      ]}>
        {/* Content stays in safe area */}
        <Text style={styles.title}>Welcome</Text>
      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#667eea',  // Extends to all edges
  },
  content: {
    flex: 1,
  },
  title: {
    color: 'white',
    fontSize: 32,
  },
});

Custom Header Component

import { View, Text, Pressable, StyleSheet } from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';

interface HeaderProps {
  title: string;
  onBack?: () => void;
}

function Header({ title, onBack }: HeaderProps) {
  const insets = useSafeAreaInsets();

  return (
    <View style={[
      styles.header,
      { paddingTop: insets.top }
    ]}>
      <View style={styles.headerContent}>
        {onBack && (
          <Pressable onPress={onBack} style={styles.backButton}>
            <Text style={styles.backText}>← Back</Text>
          </Pressable>
        )}
        <Text style={styles.title}>{title}</Text>
        <View style={styles.placeholder} />
      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  header: {
    backgroundColor: '#2196F3',
  },
  headerContent: {
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'space-between',
    height: 56,
    paddingHorizontal: 16,
  },
  backButton: {
    width: 60,
  },
  backText: {
    color: 'white',
    fontSize: 16,
  },
  title: {
    color: 'white',
    fontSize: 18,
    fontWeight: 'bold',
  },
  placeholder: {
    width: 60,
  },
});

Bottom Action Bar

import { View, Text, Pressable, StyleSheet } from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';

function BottomActionBar() {
  const insets = useSafeAreaInsets();

  return (
    <View style={[
      styles.container,
      { paddingBottom: Math.max(insets.bottom, 16) }
    ]}>
      <Pressable style={styles.button}>
        <Text style={styles.buttonText}>Add to Cart — $99</Text>
      </Pressable>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    backgroundColor: 'white',
    borderTopWidth: 1,
    borderTopColor: '#e0e0e0',
    paddingHorizontal: 16,
    paddingTop: 12,
  },
  button: {
    backgroundColor: '#2196F3',
    paddingVertical: 16,
    borderRadius: 12,
    alignItems: 'center',
  },
  buttonText: {
    color: 'white',
    fontSize: 16,
    fontWeight: 'bold',
  },
});

💡 Math.max for Minimum Padding

Use Math.max(insets.bottom, 16) to ensure at least some padding even on devices with no bottom inset. This prevents content from sitting flush against the edge.

Modal Screens

Modals often need their own safe area handling:

import { View, Modal, StyleSheet } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';

function FullScreenModal({ visible, onClose, children }) {
  return (
    <Modal
      visible={visible}
      animationType="slide"
      presentationStyle="fullScreen"
    >
      <SafeAreaView style={styles.modal} edges={['top', 'bottom']}>
        <View style={styles.header}>
          <Pressable onPress={onClose}>
            <Text>Close</Text>
          </Pressable>
        </View>
        <View style={styles.content}>
          {children}
        </View>
      </SafeAreaView>
    </Modal>
  );
}

const styles = StyleSheet.create({
  modal: {
    flex: 1,
    backgroundColor: 'white',
  },
  header: {
    padding: 16,
    borderBottomWidth: 1,
    borderBottomColor: '#e0e0e0',
  },
  content: {
    flex: 1,
  },
});

Landscape Considerations

In landscape mode, the notch may be on the left or right side:

import { View, StyleSheet, useWindowDimensions } from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';

function LandscapeAwareScreen() {
  const insets = useSafeAreaInsets();
  const { width, height } = useWindowDimensions();
  const isLandscape = width > height;

  return (
    <View style={[
      styles.container,
      {
        paddingTop: insets.top,
        paddingBottom: insets.bottom,
        // In landscape, notch creates left or right inset
        paddingLeft: Math.max(insets.left, 16),
        paddingRight: Math.max(insets.right, 16),
      }
    ]}>
      {/* Content */}
    </View>
  );
}
flowchart TD
    A[Need Safe Area?] --> B{Using Navigation
React Navigation / Expo Router?} B -->|Yes| C{Screen Type?} B -->|No| D[Wrap with SafeAreaView
edges=all] C -->|Stack screen| E[Header handles top
May need bottom for content] C -->|Tab screen| F[Tab bar handles bottom
Header handles top] C -->|Modal| G[Usually need top + bottom
Check presentationStyle] E --> H{Has scrollable content?} F --> H G --> H H -->|Yes| I[Apply insets to
contentContainerStyle] H -->|No| J[Apply insets to
container View] style D fill:#e8f5e9,stroke:#4caf50 style I fill:#e3f2fd,stroke:#1976d2 style J fill:#e3f2fd,stroke:#1976d2

Decision tree for applying safe area insets in different contexts

Hands-On Exercises

Practice makes perfect! These exercises will help you master safe areas.

Exercise 1: Simple Safe Screen

Goal: Create a screen that displays content safely on any device.

Requirements:

  • Use react-native-safe-area-context's SafeAreaView
  • Display a title "Welcome" at the top
  • Display some body text below
  • Add a button at the bottom that stays above the home indicator
💡 Hint

Use flex: 1 on the SafeAreaView and justifyContent: 'space-between' to push the button to the bottom.

✅ Solution
import { View, Text, Pressable, StyleSheet } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';

export default function SafeScreen() {
  return (
    <SafeAreaView style={styles.container}>
      <View style={styles.content}>
        <View>
          <Text style={styles.title}>Welcome</Text>
          <Text style={styles.body}>
            This content is safely positioned away from the notch 
            and home indicator on all devices.
          </Text>
        </View>
        
        <Pressable style={styles.button}>
          <Text style={styles.buttonText}>Get Started</Text>
        </Pressable>
      </View>
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#f5f5f5',
  },
  content: {
    flex: 1,
    justifyContent: 'space-between',
    padding: 20,
  },
  title: {
    fontSize: 32,
    fontWeight: 'bold',
    color: '#333',
    marginBottom: 12,
  },
  body: {
    fontSize: 16,
    color: '#666',
    lineHeight: 24,
  },
  button: {
    backgroundColor: '#2196F3',
    paddingVertical: 16,
    borderRadius: 12,
    alignItems: 'center',
  },
  buttonText: {
    color: 'white',
    fontSize: 16,
    fontWeight: 'bold',
  },
});

Exercise 2: Custom Header with Insets

Goal: Create a custom header that uses useSafeAreaInsets.

Requirements:

  • Header with colored background that extends under the status bar
  • Title text positioned below the status bar area
  • Use useSafeAreaInsets to get the correct top padding
  • Fixed height content area (56px) plus the inset
💡 Hint

Apply paddingTop: insets.top to the header container, then add your fixed-height content below that.

✅ Solution
import { View, Text, StyleSheet } from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';

export default function CustomHeaderScreen() {
  const insets = useSafeAreaInsets();

  return (
    <View style={styles.screen}>
      {/* Header with safe area */}
      <View style={[
        styles.header,
        { paddingTop: insets.top }
      ]}>
        <View style={styles.headerContent}>
          <Text style={styles.headerTitle}>My Custom Header</Text>
        </View>
      </View>
      
      {/* Body content */}
      <View style={styles.body}>
        <Text style={styles.bodyText}>
          The header background extends under the status bar,
          but the title is positioned safely below it.
        </Text>
        <Text style={styles.insetInfo}>
          Top inset: {insets.top}px
        </Text>
      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  screen: {
    flex: 1,
    backgroundColor: '#fff',
  },
  header: {
    backgroundColor: '#667eea',
  },
  headerContent: {
    height: 56,
    justifyContent: 'center',
    alignItems: 'center',
  },
  headerTitle: {
    color: 'white',
    fontSize: 18,
    fontWeight: 'bold',
  },
  body: {
    flex: 1,
    padding: 20,
  },
  bodyText: {
    fontSize: 16,
    color: '#333',
    lineHeight: 24,
    marginBottom: 20,
  },
  insetInfo: {
    fontSize: 14,
    color: '#666',
    fontStyle: 'italic',
  },
});

Exercise 3: Bottom Sheet with Safe Bottom

Goal: Create a simulated bottom sheet that respects the home indicator.

Requirements:

  • A View positioned at the bottom of the screen
  • Content area with a title and some options
  • Bottom padding that accounts for the home indicator
  • Use Math.max to ensure minimum padding even without inset
💡 Hint

Use absolute positioning to place the sheet at the bottom, and apply Math.max(insets.bottom, 20) for the bottom padding.

✅ Solution
import { View, Text, Pressable, StyleSheet } from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';

export default function BottomSheetDemo() {
  const insets = useSafeAreaInsets();

  return (
    <View style={styles.container}>
      <Text style={styles.backgroundText}>Main Content Area</Text>
      
      {/* Bottom Sheet */}
      <View style={[
        styles.bottomSheet,
        { paddingBottom: Math.max(insets.bottom, 20) }
      ]}>
        <View style={styles.handle} />
        <Text style={styles.sheetTitle}>Quick Actions</Text>
        
        {['Share', 'Save', 'Edit', 'Delete'].map((action) => (
          <Pressable key={action} style={styles.option}>
            <Text style={styles.optionText}>{action}</Text>
          </Pressable>
        ))}
      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#f0f0f0',
  },
  backgroundText: {
    textAlign: 'center',
    marginTop: 100,
    fontSize: 18,
    color: '#999',
  },
  bottomSheet: {
    position: 'absolute',
    bottom: 0,
    left: 0,
    right: 0,
    backgroundColor: 'white',
    borderTopLeftRadius: 20,
    borderTopRightRadius: 20,
    paddingTop: 12,
    paddingHorizontal: 20,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: -2 },
    shadowOpacity: 0.1,
    shadowRadius: 10,
    elevation: 10,
  },
  handle: {
    width: 40,
    height: 4,
    backgroundColor: '#ddd',
    borderRadius: 2,
    alignSelf: 'center',
    marginBottom: 16,
  },
  sheetTitle: {
    fontSize: 18,
    fontWeight: 'bold',
    marginBottom: 16,
    color: '#333',
  },
  option: {
    paddingVertical: 16,
    borderBottomWidth: 1,
    borderBottomColor: '#f0f0f0',
  },
  optionText: {
    fontSize: 16,
    color: '#333',
  },
});

Challenge: Adaptive Layout

🏆 Bonus Challenge

Goal: Create a layout that adapts to both portrait and landscape orientations with proper safe area handling.

Features:

  • Display different layouts for portrait vs landscape
  • Show the current inset values on screen
  • Apply insets correctly in both orientations
  • Use useWindowDimensions to detect orientation
✅ Solution
import { View, Text, StyleSheet, useWindowDimensions } from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';

export default function AdaptiveLayout() {
  const insets = useSafeAreaInsets();
  const { width, height } = useWindowDimensions();
  const isLandscape = width > height;

  return (
    <View style={[
      styles.container,
      {
        paddingTop: insets.top,
        paddingBottom: insets.bottom,
        paddingLeft: Math.max(insets.left, 16),
        paddingRight: Math.max(insets.right, 16),
      }
    ]}>
      <Text style={styles.title}>
        {isLandscape ? '🌄 Landscape' : '📱 Portrait'}
      </Text>
      
      <View style={[
        styles.infoBox,
        isLandscape && styles.infoBoxLandscape
      ]}>
        <Text style={styles.label}>Safe Area Insets:</Text>
        <View style={[
          styles.insetGrid,
          isLandscape && styles.insetGridLandscape
        ]}>
          <View style={styles.insetItem}>
            <Text style={styles.insetLabel}>Top</Text>
            <Text style={styles.insetValue}>{insets.top}px</Text>
          </View>
          <View style={styles.insetItem}>
            <Text style={styles.insetLabel}>Right</Text>
            <Text style={styles.insetValue}>{insets.right}px</Text>
          </View>
          <View style={styles.insetItem}>
            <Text style={styles.insetLabel}>Bottom</Text>
            <Text style={styles.insetValue}>{insets.bottom}px</Text>
          </View>
          <View style={styles.insetItem}>
            <Text style={styles.insetLabel}>Left</Text>
            <Text style={styles.insetValue}>{insets.left}px</Text>
          </View>
        </View>
      </View>
      
      <Text style={styles.dimensions}>
        Screen: {Math.round(width)} × {Math.round(height)}
      </Text>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#667eea',
  },
  title: {
    fontSize: 28,
    fontWeight: 'bold',
    color: 'white',
    textAlign: 'center',
    marginVertical: 20,
  },
  infoBox: {
    backgroundColor: 'rgba(255,255,255,0.2)',
    borderRadius: 16,
    padding: 20,
    margin: 16,
  },
  infoBoxLandscape: {
    flexDirection: 'row',
    alignItems: 'center',
  },
  label: {
    color: 'white',
    fontSize: 16,
    fontWeight: '600',
    marginBottom: 16,
  },
  insetGrid: {
    flexDirection: 'row',
    flexWrap: 'wrap',
    justifyContent: 'space-between',
  },
  insetGridLandscape: {
    flex: 1,
    marginLeft: 20,
  },
  insetItem: {
    width: '48%',
    backgroundColor: 'rgba(255,255,255,0.2)',
    borderRadius: 8,
    padding: 12,
    marginBottom: 8,
    alignItems: 'center',
  },
  insetLabel: {
    color: 'rgba(255,255,255,0.8)',
    fontSize: 12,
    marginBottom: 4,
  },
  insetValue: {
    color: 'white',
    fontSize: 20,
    fontWeight: 'bold',
  },
  dimensions: {
    color: 'rgba(255,255,255,0.7)',
    textAlign: 'center',
    marginTop: 20,
  },
});

Summary

🎉 Key Takeaways

  • Safe areas protect content from notches, status bars, and home indicators
  • Built-in SafeAreaView is iOS-only and limited in control
  • react-native-safe-area-context is the recommended solution for cross-platform
  • SafeAreaProvider must wrap your app (Expo Router includes it automatically)
  • useSafeAreaInsets hook gives you the actual inset values for calculations
  • edges prop lets you apply insets selectively (top, bottom, left, right)
  • Math.max(insets.X, 16) ensures minimum padding even without insets
  • Navigation libraries often handle safe areas — don't double-apply!

Quick Reference

// Setup (App.tsx)
import { SafeAreaProvider } from 'react-native-safe-area-context';
<SafeAreaProvider>{children}</SafeAreaProvider>

// Simple usage
import { SafeAreaView } from 'react-native-safe-area-context';
<SafeAreaView style={{ flex: 1 }}>{content}</SafeAreaView>

// Selective edges
<SafeAreaView edges={['top', 'left', 'right']}>

// Hook for manual control
import { useSafeAreaInsets } from 'react-native-safe-area-context';
const insets = useSafeAreaInsets();
// insets.top, insets.right, insets.bottom, insets.left

// Common pattern
<View style={{ 
  paddingTop: insets.top,
  paddingBottom: Math.max(insets.bottom, 16) 
}}>

🚀 What's Next?

Now that you can properly position content on any device, we'll explore Pressable — the modern way to handle touch interactions in React Native.

🛡️ Safe Areas Mastered!

Your apps will now look polished on every device — from the oldest iPhone SE to the newest iPhone with Dynamic Island. No more content hiding behind notches!