Jesse A. Green / notebook

Live demo: the DocCompiler guide library as a static, read-only snapshot (October 2026). No backend, no accounts, nothing to submit.

React Native — User Guide

Generated May 2026 from facebook/react-native at ba204fa

AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it.

React Native is Meta's open-source framework for building truly native mobile applications for iOS and Android using JavaScript and React. Rather than rendering web views, React Native translates your React components into real native platform widgets — delivering the performance and feel of a native app with the productivity of React's declarative UI model.

This guide covers everything you need to go from zero to a production-ready React Native app: installation, configuration, core concepts, common tasks, and troubleshooting for real-world issues.

Note

This guide covers React Native 0.86.x on the New Architecture (Fabric renderer, TurboModules, Codegen). The New Architecture is the default for all new projects. If you need details on internals, architecture decisions, or deployment infrastructure, see the Admin Guide (Admin section).


Table of Contents


1. Quick Start

Get a new React Native app running on a simulator or device in under five minutes.

1.1 Prerequisites at a Glance

Requirement Version
Node.js ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0
Ruby (iOS only) >= 2.6.10
Xcode (iOS) Latest stable
Android Studio (Android) Latest stable with SDK 35+

1.2 Create and Run Your First App

npx @react-native-community/cli@latest init MyFirstApp
cd MyFirstApp

Run on iOS:

npx react-native start
# In a new terminal:
npx react-native run-ios

Run on Android (ensure an emulator is running or a device is connected):

npx react-native start
# In a new terminal:
npx react-native run-android

Expected output from npx react-native start:

info Starting dev server on port 8081...

                        ▐▀▄       ▄▀▌
                        ▐ ▀▄▄▄▄▄▀ ▌
                        ▐         ▌
                        ▐         ▌
                        ▐    ▄    ▌
                        ▐   ▐ ▌   ▌
                        ▐   ▀ ▀   ▌

               Welcome to React Native 0.86
                 Dev server ready.

1.3 Edit Your App

Open App.tsx in your editor. Make a change, save, and watch it appear instantly on the device — Fast Refresh is enabled by default.

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

function App(): React.JSX.Element {
  return (
    <SafeAreaView style={styles.container}>
      <Text style={styles.title}>Hello, React Native!</Text>
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  title: {
    fontSize: 24,
    fontWeight: 'bold',
  },
});

export default App;

Tip

Shake your device (or press Cmd+D on iOS Simulator / Cmd+M on Android Emulator) to open the Dev Menu. From there you can toggle the inspector, reload the app, or open React Native DevTools.


2. Installation

2.1 System Requirements

2.1.1 All Platforms

Tool Required Version Purpose
Node.js ^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0 JavaScript runtime
npm / Yarn Bundled with Node / latest Package management
Watchman Latest (recommended) File watching for Fast Refresh
Git Latest Source control

2.1.2 iOS Development (macOS Only)

Tool Required Version Purpose
macOS Latest or previous major Host OS for iOS builds
Xcode Latest stable iOS SDK, simulator, build tools
Xcode Command Line Tools Matching Xcode xcode-select --install
Ruby >= 2.6.10 CocoaPods dependency manager
CocoaPods Latest iOS native dependency management

2.1.3 Android Development

Tool Required Version Purpose
Android Studio Latest stable Android SDK, emulator, build tools
Android SDK API 35+ Target platform
Android SDK Build-Tools Latest Compilation
Android NDK Required for native modules C++ compilation
Java Development Kit JDK 17 Gradle builds

2.2 macOS Setup

2.2.1 Install Homebrew Dependencies

brew install node watchman

Verify Node version:

node --version
# Expected: v22.x.x or v20.x.x (matching required range)

2.2.2 Install Ruby and CocoaPods (iOS)

macOS ships with Ruby, but using a version manager is recommended:

brew install rbenv ruby-build
rbenv install 3.3.0
rbenv global 3.3.0

# Verify
ruby --version
# Expected: ruby 3.3.0 or higher

gem install cocoapods

2.2.3 Install Xcode (iOS)

  1. Install Xcode from the Mac App Store
  2. Open Xcode and accept the license agreement
  3. Install Command Line Tools:
xcode-select --install
  1. Verify the installation:
xcodebuild -version
# Expected: Xcode 16.x, Build version ...

2.2.4 Install Android Studio (Android)

  1. Download Android Studio from https://developer.android.com/studio
  2. During installation, ensure these are checked:
    • Android SDK
    • Android SDK Platform
    • Android Virtual Device
  3. Open Android Studio > Settings > SDK Manager
  4. Install SDK Platform for your target API level (35+)
  5. Install NDK (Side by side) under SDK Tools tab

2.2.5 Configure Environment Variables

Add to your ~/.zshrc or ~/.bash_profile:

export ANDROID_HOME=$HOME/Library/Android/sdk
export PATH=$PATH:$ANDROID_HOME/emulator
export PATH=$PATH:$ANDROID_HOME/platform-tools

Reload your shell:

source ~/.zshrc

2.3 Windows Setup (Android Only)

Important

iOS development requires macOS. On Windows, you can only build for Android. Use a Mac or a cloud-based Mac service for iOS builds.

2.3.1 Install Node.js

Download the LTS installer from https://nodejs.org/ or use a version manager:

# Using Chocolatey
choco install nodejs-lts

# Verify
node --version

2.3.2 Install Android Studio

  1. Download from https://developer.android.com/studio
  2. During install, check: Android SDK, Android SDK Platform, Android Virtual Device
  3. Install SDK Platform API 35+ from SDK Manager
  4. Install NDK from SDK Tools tab

2.3.3 Configure Environment Variables

Set the following system environment variables:

Variable Value
ANDROID_HOME C:\Users\<user>\AppData\Local\Android\Sdk
Add to Path %ANDROID_HOME%\emulator
Add to Path %ANDROID_HOME%\platform-tools

2.4 Linux Setup (Android Only)

# Install Node.js (using NodeSource)
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
sudo apt-get install -y nodejs

# Install Watchman
sudo apt-get install watchman

# Install JDK 17
sudo apt-get install openjdk-17-jdk

Then install Android Studio as described in section 2.3.2.

2.5 Creating a New Project

npx @react-native-community/cli@latest init MyProject

Expected output:

✔ Downloading template
✔ Copying template
✔ Processing template
✔ Installing dependencies
✔ Installing CocoaPods dependencies (iOS)

  Run instructions for iOS:
    npx react-native run-ios

  Run instructions for Android:
    npx react-native run-android

2.5.2 With a Specific Version

npx @react-native-community/cli@latest init MyProject --version 0.86.0

2.5.3 With a Custom Template

npx @react-native-community/cli@latest init MyProject --template react-native-template-typescript

2.6 Project Structure

After initialization, your project will have this structure:

MyProject/
├── __tests__/           # Jest test files
├── android/             # Android native project
│   ├── app/
│   │   ├── build.gradle
│   │   └── src/
│   ├── build.gradle
│   ├── gradle.properties
│   └── settings.gradle
├── ios/                 # iOS native project
│   ├── MyProject/
│   ├── Podfile
│   └── MyProject.xcworkspace
├── node_modules/
├── App.tsx              # Root component
├── app.json             # App configuration
├── babel.config.js      # Babel configuration
├── index.js             # App entry point
├── metro.config.js      # Metro bundler configuration
├── package.json
└── tsconfig.json        # TypeScript configuration

2.7 Verifying Your Setup

Run the built-in environment check:

npx react-native doctor

This checks for Node, Watchman, Xcode, CocoaPods, Android Studio, Android SDK, and JDK. Fix any issues it reports before proceeding.


3. Configuration

3.1 Metro Bundler Configuration

Metro is React Native's JavaScript bundler. The default configuration is provided by @react-native/metro-config.

3.1.1 Default Configuration

Your project's metro.config.js:

const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config');

const config = {};

module.exports = mergeConfig(getDefaultConfig(__dirname), config);

The default configuration includes:

Setting Default Value Purpose
Resolver fields ['react-native', 'browser', 'main'] Module resolution order
Platforms ['android', 'ios'] Supported target platforms
Inline requires true Performance optimization
Port 8081 Dev server port

3.1.2 Changing the Dev Server Port

Set the RCT_METRO_PORT environment variable:

RCT_METRO_PORT=8088 npx react-native start

Or configure it in metro.config.js:

const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config');

const config = {
  server: {
    port: 8088,
  },
};

module.exports = mergeConfig(getDefaultConfig(__dirname), config);

3.1.3 Adding Asset Extensions

To support additional asset types beyond the defaults:

const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config');

const defaultConfig = getDefaultConfig(__dirname);

const config = {
  resolver: {
    assetExts: [...defaultConfig.resolver.assetExts, 'obj', 'mtl', 'gltf'],
  },
};

module.exports = mergeConfig(defaultConfig, config);

3.1.4 Adding Source Extensions

To support additional source file extensions:

const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config');

const defaultConfig = getDefaultConfig(__dirname);

const config = {
  resolver: {
    sourceExts: [...defaultConfig.resolver.sourceExts, 'cjs', 'mjs'],
  },
};

module.exports = mergeConfig(defaultConfig, config);

3.1.5 Configuring Module Aliases

const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config');
const path = require('path');

const config = {
  resolver: {
    extraNodeModules: {
      '@app': path.resolve(__dirname, 'src'),
      '@components': path.resolve(__dirname, 'src/components'),
      '@utils': path.resolve(__dirname, 'src/utils'),
    },
  },
};

module.exports = mergeConfig(getDefaultConfig(__dirname), config);

3.2 Babel Configuration

React Native uses @react-native/babel-preset which includes support for Flow stripping, class properties, async/await, optional chaining, nullish coalescing, and more.

3.2.1 Default Configuration

Your babel.config.js:

module.exports = {
  presets: ['module:@react-native/babel-preset'],
};

3.2.2 Adding Plugins

module.exports = {
  presets: ['module:@react-native/babel-preset'],
  plugins: [
    'react-native-reanimated/plugin', // Must be last
  ],
};

Warning

If you use react-native-reanimated, its Babel plugin must be listed last in the plugins array. Placing it elsewhere causes subtle runtime errors that are difficult to diagnose.

3.2.3 Environment-Specific Configuration

module.exports = (api) => {
  const isProd = api.env('production');

  return {
    presets: ['module:@react-native/babel-preset'],
    plugins: [
      isProd && 'transform-remove-console',
    ].filter(Boolean),
  };
};

3.3 TypeScript Configuration

Default tsconfig.json:

{
  "extends": "@react-native/typescript-config/tsconfig.json",
  "compilerOptions": {
    "strict": true
  }
}

3.3.1 Path Aliases with TypeScript

If you configure Metro module aliases (section 3.1.5), mirror them in tsconfig.json:

{
  "extends": "@react-native/typescript-config/tsconfig.json",
  "compilerOptions": {
    "strict": true,
    "baseUrl": ".",
    "paths": {
      "@app/*": ["src/*"],
      "@components/*": ["src/components/*"],
      "@utils/*": ["src/utils/*"]
    }
  }
}

3.4 ESLint Configuration

React Native provides @react-native/eslint-config and @react-native/eslint-plugin for code quality.

3.4.1 Default Setup

Your .eslintrc.js:

module.exports = {
  root: true,
  extends: '@react-native',
};

This extends @react-native/eslint-config, which bundles rules for React, React Native, Jest, Flow/TypeScript, and Prettier.

3.4.2 Adding Custom Rules

module.exports = {
  root: true,
  extends: '@react-native',
  rules: {
    'no-console': 'warn',
    'react-native/no-inline-styles': 'warn',
    'react/no-unstable-nested-components': 'error',
  },
};

3.5 Jest Configuration

React Native uses @react-native/jest-preset for testing.

3.5.1 Default Setup

In your package.json:

{
  "jest": {
    "preset": "react-native"
  }
}

Or in a standalone jest.config.js:

module.exports = {
  preset: 'react-native',
  setupFilesAfterSetup: ['./jest.setup.js'],
  transformIgnorePatterns: [
    'node_modules/(?!(react-native|@react-native|@react-navigation)/)',
  ],
};

3.5.2 Module Name Mapping

If you use path aliases, configure Jest to resolve them:

module.exports = {
  preset: 'react-native',
  moduleNameMapper: {
    '^@app/(.*)$': '<rootDir>/src/$1',
    '^@components/(.*)$': '<rootDir>/src/components/$1',
    '^@utils/(.*)$': '<rootDir>/src/utils/$1',
  },
};

3.6 Prettier Configuration

React Native uses these Prettier settings internally:

{
  "arrowParens": "avoid",
  "singleQuote": true,
  "trailingComma": "all"
}

Create a .prettierrc in your project root with these or your preferred settings to maintain consistent formatting.

3.7 Android Configuration

3.7.1 Gradle Plugin

React Native's Gradle plugin is applied in your Android project. The key plugins are:

  • com.facebook.react — applied to your app/build.gradle
  • com.facebook.react.rootproject — applied to the root build.gradle

3.7.2 Gradle Properties

Key properties in android/gradle.properties:

# Enable the New Architecture (default: true in 0.86)
newArchEnabled=true

# Enable Hermes JS engine (default: true)
hermesEnabled=true

# Configure build variants
reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64

3.7.3 App-Level build.gradle

apply plugin: "com.facebook.react"

react {
    // Configures bundling for release builds
    // entryFile = file("index.js")
    // bundleCommand = "bundle"
}

android {
    namespace "com.myproject"
    compileSdk 35
    defaultConfig {
        applicationId "com.myproject"
        minSdk 24
        targetSdk 35
        versionCode 1
        versionName "1.0"
    }
}

3.8 iOS Configuration

3.8.1 Podfile

The iOS Podfile manages native dependencies via CocoaPods:

platform :ios, '15.1'

target 'MyProject' do
  config = use_native_modules!

  use_react_native!(
    :path => config[:reactNativePath],
    :hermes_enabled => true,
    :fabric_enabled => true,
  )

  post_install do |installer|
    react_native_post_install(installer)
  end
end

3.8.2 Installing iOS Dependencies

cd ios
bundle install     # Install Ruby gems (if using Bundler)
pod install        # Install CocoaPods dependencies
cd ..

Expected output:

Analyzing dependencies
Downloading dependencies
Installing React-Core (0.86.0)
Installing hermes-engine (0.86.0)
...
Pod installation complete!

3.9 App Configuration (app.json)

The app.json file provides basic app metadata:

{
  "name": "MyProject",
  "displayName": "My Project"
}

This is read by index.js to register the app:

import {AppRegistry} from 'react-native';
import App from './App';
import {name as appName} from './app.json';

AppRegistry.registerComponent(appName, () => App);

4. Core Usage

4.1 Components

React Native provides a set of core components that map directly to native platform views.

4.1.1 Basic Components

Component Purpose Native Equivalent
View Container, layout UIView (iOS), ViewGroup (Android)
Text Display text UILabel (iOS), TextView (Android)
Image Display images UIImageView (iOS), ImageView (Android)
TextInput Text entry UITextField (iOS), EditText (Android)
ScrollView Scrollable container UIScrollView (iOS), ScrollView (Android)
Button Basic button Native button
import React from 'react';
import {View, Text, Image, TextInput, Button, ScrollView} from 'react-native';

function BasicComponents(): React.JSX.Element {
  return (
    <ScrollView>
      <View style={{padding: 20}}>
        <Text>Hello, world!</Text>
        <Image
          source={{uri: 'https://reactnative.dev/img/tiny_logo.png'}}
          style={{width: 50, height: 50}}
        />
        <TextInput placeholder="Type here..." />
        <Button title="Press Me" onPress={() => console.log('Pressed!')} />
      </View>
    </ScrollView>
  );
}

4.1.2 Layout with View and Flexbox

React Native uses Flexbox for layout. The default flexDirection is column (unlike web CSS which defaults to row).

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

function FlexboxExample(): React.JSX.Element {
  return (
    <View style={styles.container}>
      <View style={styles.row}>
        <View style={[styles.box, {backgroundColor: '#e74c3c'}]}>
          <Text style={styles.label}>1</Text>
        </View>
        <View style={[styles.box, {backgroundColor: '#3498db'}]}>
          <Text style={styles.label}>2</Text>
        </View>
        <View style={[styles.box, {backgroundColor: '#2ecc71'}]}>
          <Text style={styles.label}>3</Text>
        </View>
      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    padding: 20,
  },
  row: {
    flexDirection: 'row',
    justifyContent: 'space-between',
  },
  box: {
    width: 80,
    height: 80,
    justifyContent: 'center',
    alignItems: 'center',
    borderRadius: 8,
  },
  label: {
    color: 'white',
    fontSize: 18,
    fontWeight: 'bold',
  },
});

Key Flexbox properties:

Property Values Default
flexDirection column, row, column-reverse, row-reverse column
justifyContent flex-start, flex-end, center, space-between, space-around, space-evenly flex-start
alignItems flex-start, flex-end, center, stretch, baseline stretch
flex Number —
flexWrap wrap, nowrap nowrap
gap Number —
rowGap Number —
columnGap Number —

4.1.3 Text and Typography

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

function Typography(): React.JSX.Element {
  return (
    <>
      <Text style={styles.heading}>Heading</Text>
      <Text style={styles.body}>
        Body text with <Text style={styles.bold}>bold</Text> and{' '}
        <Text style={styles.italic}>italic</Text> spans.
      </Text>
      <Text style={styles.link} onPress={() => console.log('Tapped!')}>
        Tappable text
      </Text>
      <Text numberOfLines={2} ellipsizeMode="tail">
        This is a very long paragraph that will be truncated after two lines
        because we set numberOfLines to 2 and ellipsizeMode to tail so it
        shows an ellipsis at the end.
      </Text>
    </>
  );
}

const styles = StyleSheet.create({
  heading: {
    fontSize: 28,
    fontWeight: 'bold',
    marginBottom: 8,
  },
  body: {
    fontSize: 16,
    lineHeight: 24,
    color: '#333',
  },
  bold: {
    fontWeight: 'bold',
  },
  italic: {
    fontStyle: 'italic',
  },
  link: {
    color: '#3498db',
    textDecorationLine: 'underline',
  },
});

4.1.4 Images

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

function ImageExamples(): React.JSX.Element {
  return (
    <View>
      {/* Local image (require) */}
      <Image source={require('./assets/logo.png')} style={styles.localImage} />

      {/* Remote image (must specify dimensions) */}
      <Image
        source={{uri: 'https://example.com/photo.jpg'}}
        style={styles.remoteImage}
        resizeMode="cover"
      />

      {/* Background image */}
      <ImageBackground
        source={require('./assets/background.png')}
        style={styles.background}>
        <Text style={styles.overlayText}>Overlay text</Text>
      </ImageBackground>
    </View>
  );
}

const styles = StyleSheet.create({
  localImage: {
    width: 100,
    height: 100,
  },
  remoteImage: {
    width: 200,
    height: 150,
    borderRadius: 8,
  },
  background: {
    width: '100%',
    height: 200,
    justifyContent: 'center',
    alignItems: 'center',
  },
  overlayText: {
    color: 'white',
    fontSize: 24,
    fontWeight: 'bold',
  },
});

Image resizeMode options:

Mode Behavior
cover Scale to fill, may crop
contain Scale to fit, may letterbox
stretch Stretch to fill exactly
repeat Tile the image (iOS only)
center Center without scaling

4.1.5 TextInput

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

function FormExample(): React.JSX.Element {
  const [name, setName] = useState('');
  const [email, setEmail] = useState('');
  const [bio, setBio] = useState('');

  return (
    <View style={styles.form}>
      <Text style={styles.label}>Name</Text>
      <TextInput
        style={styles.input}
        value={name}
        onChangeText={setName}
        placeholder="Your name"
        autoCapitalize="words"
        returnKeyType="next"
      />

      <Text style={styles.label}>Email</Text>
      <TextInput
        style={styles.input}
        value={email}
        onChangeText={setEmail}
        placeholder="[email removed]"
        keyboardType="email-address"
        autoCapitalize="none"
        autoCorrect={false}
        returnKeyType="next"
      />

      <Text style={styles.label}>Bio</Text>
      <TextInput
        style={[styles.input, styles.multiline]}
        value={bio}
        onChangeText={setBio}
        placeholder="Tell us about yourself..."
        multiline
        numberOfLines={4}
        textAlignVertical="top"
      />
    </View>
  );
}

const styles = StyleSheet.create({
  form: {
    padding: 20,
  },
  label: {
    fontSize: 14,
    fontWeight: '600',
    marginBottom: 4,
    color: '#333',
  },
  input: {
    borderWidth: 1,
    borderColor: '#ddd',
    borderRadius: 8,
    padding: 12,
    marginBottom: 16,
    fontSize: 16,
  },
  multiline: {
    height: 100,
  },
});

Keyboard types available via keyboardType:

Value Use Case
default Standard text
email-address Email input
numeric Numbers only
phone-pad Phone number
decimal-pad Decimal numbers
number-pad Integer numbers
url URL input

4.2 Styling

4.2.1 StyleSheet API

Always use StyleSheet.create() for performance — it validates styles at creation time and creates optimized references:

import {StyleSheet} from 'react-native';

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

4.2.2 Combining Styles

// Array syntax — later styles override earlier ones
<View style={[styles.base, styles.active]} />

// Conditional styles
<View style={[styles.base, isActive && styles.active]} />

// Inline overrides
<View style={[styles.base, {marginTop: 10}]} />

4.2.3 Platform-Specific Styles

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

const styles = StyleSheet.create({
  shadow: {
    ...Platform.select({
      ios: {
        shadowColor: '#000',
        shadowOffset: {width: 0, height: 2},
        shadowOpacity: 0.25,
        shadowRadius: 4,
      },
      android: {
        elevation: 5,
      },
    }),
  },
});

4.2.4 Common Style Properties

React Native styles are a subset of CSS, written in camelCase. Key differences from web CSS:

Category Properties
Layout flex, flexDirection, justifyContent, alignItems, alignSelf, position, top, left, right, bottom, width, height, minWidth, maxWidth, minHeight, maxHeight, margin*, padding*, gap
Visual backgroundColor, opacity, borderWidth, borderColor, borderRadius, borderStyle
Text color, fontSize, fontWeight, fontStyle, fontFamily, lineHeight, textAlign, textDecorationLine, textTransform, letterSpacing
Shadow (iOS) shadowColor, shadowOffset, shadowOpacity, shadowRadius
Shadow (Android) elevation
Transform transform: [{rotate: '45deg'}, {scale: 1.5}]

Note

All dimensions in React Native are unitless and represent density-independent pixels (dp). There is no px, em, rem, or % unit (except for percentage strings like '50%' for width/height). This ensures consistent sizing across different screen densities.

4.3 Lists and Scrolling

4.3.1 FlatList — Virtualized List

FlatList is the primary component for rendering long, scrollable lists. It only renders items currently visible on screen, making it efficient for large datasets.

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

interface Item {
  id: string;
  title: string;
}

const DATA: Item[] = Array.from({length: 1000}, (_, i) => ({
  id: String(i),
  title: `Item ${i + 1}`,
}));

function ListExample(): React.JSX.Element {
  const renderItem = ({item}: {item: Item}) => (
    <View style={styles.item}>
      <Text style={styles.itemText}>{item.title}</Text>
    </View>
  );

  return (
    <FlatList
      data={DATA}
      renderItem={renderItem}
      keyExtractor={item => item.id}
      ItemSeparatorComponent={() => <View style={styles.separator} />}
      ListHeaderComponent={() => (
        <Text style={styles.header}>All Items</Text>
      )}
      ListEmptyComponent={() => (
        <Text style={styles.empty}>No items found</Text>
      )}
    />
  );
}

const styles = StyleSheet.create({
  item: {
    padding: 16,
    backgroundColor: '#fff',
  },
  itemText: {
    fontSize: 16,
  },
  separator: {
    height: 1,
    backgroundColor: '#eee',
  },
  header: {
    fontSize: 20,
    fontWeight: 'bold',
    padding: 16,
  },
  empty: {
    padding: 20,
    textAlign: 'center',
    color: '#999',
  },
});

4.3.2 SectionList — Grouped Data

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

const SECTIONS = [
  {
    title: 'Fruits',
    data: ['Apple', 'Banana', 'Cherry', 'Date'],
  },
  {
    title: 'Vegetables',
    data: ['Artichoke', 'Broccoli', 'Carrot', 'Daikon'],
  },
];

function SectionListExample(): React.JSX.Element {
  return (
    <SectionList
      sections={SECTIONS}
      keyExtractor={(item, index) => item + index}
      renderItem={({item}) => (
        <View style={styles.item}>
          <Text>{item}</Text>
        </View>
      )}
      renderSectionHeader={({section: {title}}) => (
        <View style={styles.sectionHeader}>
          <Text style={styles.sectionTitle}>{title}</Text>
        </View>
      )}
      stickySectionHeadersEnabled
    />
  );
}

const styles = StyleSheet.create({
  item: {
    padding: 16,
    backgroundColor: '#fff',
  },
  sectionHeader: {
    padding: 12,
    backgroundColor: '#f5f5f5',
  },
  sectionTitle: {
    fontSize: 16,
    fontWeight: 'bold',
  },
});

4.3.3 Pull-to-Refresh

import React, {useState, useCallback} from 'react';
import {FlatList, RefreshControl} from 'react-native';

function RefreshableList(): React.JSX.Element {
  const [refreshing, setRefreshing] = useState(false);
  const [data, setData] = useState(initialData);

  const onRefresh = useCallback(async () => {
    setRefreshing(true);
    const newData = await fetchData();
    setData(newData);
    setRefreshing(false);
  }, []);

  return (
    <FlatList
      data={data}
      renderItem={renderItem}
      keyExtractor={item => item.id}
      refreshControl={
        <RefreshControl refreshing={refreshing} onRefresh={onRefresh} />
      }
    />
  );
}

4.3.4 Infinite Scroll / Pagination

function PaginatedList(): React.JSX.Element {
  const [data, setData] = useState<Item[]>([]);
  const [page, setPage] = useState(1);
  const [loading, setLoading] = useState(false);

  const loadMore = useCallback(async () => {
    if (loading) return;
    setLoading(true);
    const newItems = await fetchPage(page + 1);
    setData(prev => [...prev, ...newItems]);
    setPage(prev => prev + 1);
    setLoading(false);
  }, [page, loading]);

  return (
    <FlatList
      data={data}
      renderItem={renderItem}
      keyExtractor={item => item.id}
      onEndReached={loadMore}
      onEndReachedThreshold={0.5}
      ListFooterComponent={loading ? <ActivityIndicator /> : null}
    />
  );
}

4.4 Handling User Input

4.4.1 Touchables and Pressable

import React from 'react';
import {
  Pressable,
  TouchableOpacity,
  TouchableHighlight,
  Text,
  StyleSheet,
} from 'react-native';

function ButtonExamples(): React.JSX.Element {
  return (
    <>
      {/* Pressable — most flexible, recommended */}
      <Pressable
        style={({pressed}) => [
          styles.button,
          pressed && styles.buttonPressed,
        ]}
        onPress={() => console.log('Pressed')}
        onLongPress={() => console.log('Long pressed')}
        hitSlop={10}>
        <Text style={styles.buttonText}>Pressable</Text>
      </Pressable>

      {/* TouchableOpacity — fades on press */}
      <TouchableOpacity
        style={styles.button}
        activeOpacity={0.7}
        onPress={() => console.log('Pressed')}>
        <Text style={styles.buttonText}>TouchableOpacity</Text>
      </TouchableOpacity>

      {/* TouchableHighlight — highlights on press */}
      <TouchableHighlight
        style={styles.button}
        underlayColor="#ddd"
        onPress={() => console.log('Pressed')}>
        <Text style={styles.buttonText}>TouchableHighlight</Text>
      </TouchableHighlight>
    </>
  );
}

const styles = StyleSheet.create({
  button: {
    backgroundColor: '#3498db',
    paddingVertical: 12,
    paddingHorizontal: 24,
    borderRadius: 8,
    marginBottom: 12,
  },
  buttonPressed: {
    backgroundColor: '#2980b9',
    transform: [{scale: 0.98}],
  },
  buttonText: {
    color: '#fff',
    fontSize: 16,
    fontWeight: '600',
    textAlign: 'center',
  },
});

4.4.2 Gesture Handling

import React from 'react';
import {View, PanResponder, Animated} from 'react-native';

function DraggableBox(): React.JSX.Element {
  const pan = React.useRef(new Animated.ValueXY()).current;

  const panResponder = React.useRef(
    PanResponder.create({
      onMoveShouldSetPanResponder: () => true,
      onPanResponderMove: Animated.event(
        [null, {dx: pan.x, dy: pan.y}],
        {useNativeDriver: false},
      ),
      onPanResponderRelease: () => {
        Animated.spring(pan, {
          toValue: {x: 0, y: 0},
          useNativeDriver: true,
        }).start();
      },
    }),
  ).current;

  return (
    <Animated.View
      style={[
        {
          width: 100,
          height: 100,
          backgroundColor: '#3498db',
          borderRadius: 8,
          transform: [{translateX: pan.x}, {translateY: pan.y}],
        },
      ]}
      {...panResponder.panHandlers}
    />
  );
}

4.5 Navigation

React Native does not include a built-in navigation system. The community-standard library is React Navigation (@react-navigation).

4.5.1 Installing React Navigation

npm install @react-navigation/native @react-navigation/native-stack

# Required peer dependencies
npm install react-native-screens react-native-safe-area-context

For iOS, install pods:

cd ios && pod install && cd ..

4.5.2 Stack Navigation

import React from 'react';
import {NavigationContainer} from '@react-navigation/native';
import {createNativeStackNavigator} from '@react-navigation/native-stack';
import {Button, Text, View} from 'react-native';

type RootStackParamList = {
  Home: undefined;
  Details: {itemId: number};
};

const Stack = createNativeStackNavigator<RootStackParamList>();

function HomeScreen({navigation}: any): React.JSX.Element {
  return (
    <View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
      <Text>Home Screen</Text>
      <Button
        title="Go to Details"
        onPress={() => navigation.navigate('Details', {itemId: 42})}
      />
    </View>
  );
}

function DetailsScreen({route}: any): React.JSX.Element {
  const {itemId} = route.params;
  return (
    <View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
      <Text>Details for item {itemId}</Text>
    </View>
  );
}

function App(): React.JSX.Element {
  return (
    <NavigationContainer>
      <Stack.Navigator initialRouteName="Home">
        <Stack.Screen name="Home" component={HomeScreen} />
        <Stack.Screen name="Details" component={DetailsScreen} />
      </Stack.Navigator>
    </NavigationContainer>
  );
}

export default App;

4.5.3 Tab Navigation

npm install @react-navigation/bottom-tabs
import {createBottomTabNavigator} from '@react-navigation/bottom-tabs';

const Tab = createBottomTabNavigator();

function App(): React.JSX.Element {
  return (
    <NavigationContainer>
      <Tab.Navigator>
        <Tab.Screen name="Home" component={HomeScreen} />
        <Tab.Screen name="Settings" component={SettingsScreen} />
        <Tab.Screen name="Profile" component={ProfileScreen} />
      </Tab.Navigator>
    </NavigationContainer>
  );
}

4.5.4 Drawer Navigation

npm install @react-navigation/drawer react-native-gesture-handler react-native-reanimated
import {createDrawerNavigator} from '@react-navigation/drawer';

const Drawer = createDrawerNavigator();

function App(): React.JSX.Element {
  return (
    <NavigationContainer>
      <Drawer.Navigator>
        <Drawer.Screen name="Home" component={HomeScreen} />
        <Drawer.Screen name="Settings" component={SettingsScreen} />
      </Drawer.Navigator>
    </NavigationContainer>
  );
}

4.6 Networking

4.6.1 Fetch API

React Native includes the standard fetch API:

async function fetchUsers() {
  try {
    const response = await fetch('https://api.example.com/users');
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    const data = await response.json();
    return data;
  } catch (error) {
    console.error('Fetch failed:', error);
    throw error;
  }
}

4.6.2 POST Requests

async function createUser(name: string, email: string) {
  const response = await fetch('https://api.example.com/users', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      Authorization: 'Bearer YOUR_TOKEN',
    },
    body: JSON.stringify({name, email}),
  });
  return response.json();
}

4.6.3 Using Data in Components

import React, {useState, useEffect} from 'react';
import {FlatList, Text, View, ActivityIndicator} from 'react-native';

interface User {
  id: number;
  name: string;
}

function UserList(): React.JSX.Element {
  const [users, setUsers] = useState<User[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    fetch('https://jsonplaceholder.typicode.com/users')
      .then(res => res.json())
      .then(data => {
        setUsers(data);
        setLoading(false);
      })
      .catch(err => {
        setError(err.message);
        setLoading(false);
      });
  }, []);

  if (loading) return <ActivityIndicator size="large" />;
  if (error) return <Text>Error: {error}</Text>;

  return (
    <FlatList
      data={users}
      keyExtractor={item => String(item.id)}
      renderItem={({item}) => (
        <View style={{padding: 16}}>
          <Text>{item.name}</Text>
        </View>
      )}
    />
  );
}

4.6.4 WebSocket

const ws = new WebSocket('wss://echo.websocket.org');

ws.onopen = () => {
  ws.send('Hello from React Native!');
};

ws.onmessage = (event) => {
  console.log('Received:', event.data);
};

ws.onerror = (error) => {
  console.error('WebSocket error:', error);
};

ws.onclose = () => {
  console.log('WebSocket closed');
};

4.7 State Management

4.7.1 useState

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

function Counter(): React.JSX.Element {
  const [count, setCount] = useState(0);

  return (
    <View>
      <Text>Count: {count}</Text>
      <Button title="Increment" onPress={() => setCount(c => c + 1)} />
      <Button title="Decrement" onPress={() => setCount(c => c - 1)} />
      <Button title="Reset" onPress={() => setCount(0)} />
    </View>
  );
}

4.7.2 useReducer

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

type State = {count: number};
type Action = {type: 'increment'} | {type: 'decrement'} | {type: 'reset'};

function reducer(state: State, action: Action): State {
  switch (action.type) {
    case 'increment':
      return {count: state.count + 1};
    case 'decrement':
      return {count: state.count - 1};
    case 'reset':
      return {count: 0};
  }
}

function Counter(): React.JSX.Element {
  const [state, dispatch] = useReducer(reducer, {count: 0});

  return (
    <View>
      <Text>Count: {state.count}</Text>
      <Button title="+" onPress={() => dispatch({type: 'increment'})} />
      <Button title="-" onPress={() => dispatch({type: 'decrement'})} />
      <Button title="Reset" onPress={() => dispatch({type: 'reset'})} />
    </View>
  );
}

4.7.3 Context API

import React, {createContext, useContext, useState} from 'react';

interface ThemeContextType {
  isDark: boolean;
  toggle: () => void;
}

const ThemeContext = createContext<ThemeContextType>({
  isDark: false,
  toggle: () => {},
});

function ThemeProvider({children}: {children: React.ReactNode}) {
  const [isDark, setIsDark] = useState(false);
  const toggle = () => setIsDark(prev => !prev);

  return (
    <ThemeContext.Provider value={{isDark, toggle}}>
      {children}
    </ThemeContext.Provider>
  );
}

function useTheme() {
  return useContext(ThemeContext);
}

// Usage in a component
function ThemedScreen(): React.JSX.Element {
  const {isDark, toggle} = useTheme();

  return (
    <View style={{backgroundColor: isDark ? '#333' : '#fff', flex: 1}}>
      <Text style={{color: isDark ? '#fff' : '#333'}}>
        Current theme: {isDark ? 'Dark' : 'Light'}
      </Text>
      <Button title="Toggle Theme" onPress={toggle} />
    </View>
  );
}

4.8 Animations

4.8.1 Animated API

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

function FadeInView({children}: {children: React.ReactNode}): React.JSX.Element {
  const fadeAnim = useRef(new Animated.Value(0)).current;

  useEffect(() => {
    Animated.timing(fadeAnim, {
      toValue: 1,
      duration: 800,
      useNativeDriver: true,
    }).start();
  }, [fadeAnim]);

  return (
    <Animated.View style={{opacity: fadeAnim}}>
      {children}
    </Animated.View>
  );
}

4.8.2 Animation Types

// Spring animation
Animated.spring(value, {
  toValue: 1,
  friction: 5,
  tension: 40,
  useNativeDriver: true,
}).start();

// Timing animation with easing
import {Easing} from 'react-native';

Animated.timing(value, {
  toValue: 1,
  duration: 500,
  easing: Easing.bezier(0.25, 0.1, 0.25, 1),
  useNativeDriver: true,
}).start();

// Decay animation (momentum-based)
Animated.decay(value, {
  velocity: 0.95,
  deceleration: 0.997,
  useNativeDriver: true,
}).start();

4.8.3 Composing Animations

// Sequential
Animated.sequence([
  Animated.timing(opacity, {toValue: 1, duration: 300, useNativeDriver: true}),
  Animated.timing(translateY, {toValue: 0, duration: 300, useNativeDriver: true}),
]).start();

// Parallel
Animated.parallel([
  Animated.timing(opacity, {toValue: 1, duration: 300, useNativeDriver: true}),
  Animated.timing(scale, {toValue: 1, duration: 300, useNativeDriver: true}),
]).start();

// Stagger
Animated.stagger(100, [
  Animated.timing(item1Opacity, {toValue: 1, duration: 300, useNativeDriver: true}),
  Animated.timing(item2Opacity, {toValue: 1, duration: 300, useNativeDriver: true}),
  Animated.timing(item3Opacity, {toValue: 1, duration: 300, useNativeDriver: true}),
]).start();

// Loop
Animated.loop(
  Animated.sequence([
    Animated.timing(rotation, {toValue: 1, duration: 1000, useNativeDriver: true}),
    Animated.timing(rotation, {toValue: 0, duration: 1000, useNativeDriver: true}),
  ]),
).start();

4.8.4 Interpolation

const spinValue = useRef(new Animated.Value(0)).current;

const spin = spinValue.interpolate({
  inputRange: [0, 1],
  outputRange: ['0deg', '360deg'],
});

const backgroundColor = animValue.interpolate({
  inputRange: [0, 0.5, 1],
  outputRange: ['#e74c3c', '#f39c12', '#2ecc71'],
});

return (
  <Animated.View
    style={{
      transform: [{rotate: spin}],
      backgroundColor,
    }}
  />
);

4.9 Platform-Specific Code

4.9.1 Platform Module

import {Platform} from 'react-native';

// Simple check
if (Platform.OS === 'ios') {
  // iOS-specific code
} else if (Platform.OS === 'android') {
  // Android-specific code
}

// Version check
if (Platform.OS === 'android' && Platform.Version >= 33) {
  // Android 13+ specific code
}

// Platform.select
const styles = StyleSheet.create({
  container: {
    ...Platform.select({
      ios: {paddingTop: 44},
      android: {paddingTop: 0},
      default: {paddingTop: 0},
    }),
  },
});

4.9.2 Platform-Specific Files

React Native automatically picks platform-specific files based on extension:

Button.ios.tsx      ← Used on iOS
Button.android.tsx  ← Used on Android
Button.tsx          ← Fallback for other platforms

Import normally — Metro resolves the correct file:

import Button from './Button'; // Resolves to .ios.tsx or .android.tsx

4.10 Safe Area Handling

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

function App(): React.JSX.Element {
  return (
    <>
      <StatusBar barStyle="dark-content" backgroundColor="#fff" />
      <SafeAreaView style={styles.container}>
        {/* Your content here — respects notch, status bar, home indicator */}
      </SafeAreaView>
    </>
  );
}

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

For more control, use the community react-native-safe-area-context library:

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

function Screen(): React.JSX.Element {
  const insets = useSafeAreaInsets();

  return (
    <View style={{paddingTop: insets.top, paddingBottom: insets.bottom}}>
      {/* Content */}
    </View>
  );
}

function App(): React.JSX.Element {
  return (
    <SafeAreaProvider>
      <Screen />
    </SafeAreaProvider>
  );
}

4.11 Debugging

4.11.1 React Native DevTools

React Native ships with a built-in debugger based on Chrome DevTools. Access it via the Dev Menu (shake device or Cmd+D / Cmd+M).

The inspector proxy exposes these endpoints on the Metro dev server:

Endpoint Purpose
/json List available debug targets
/json/list Same as /json
/json/version Version information
/inspector/device Device connection endpoint
/inspector/debug Debugger connection endpoint

4.11.2 Console Logging

console.log('Info message');
console.warn('Warning message');  // Yellow box
console.error('Error message');   // Red box

Logs appear in:

  • Metro terminal output
  • React Native DevTools console
  • Xcode console (iOS)
  • Android Studio Logcat (Android): filter by ReactNativeJS

4.11.3 React Developer Tools

Install the standalone React DevTools:

npx react-devtools

This opens a window that connects to your running app, allowing you to inspect the component tree, props, state, and hooks.

4.11.4 Network Debugging

To inspect network requests, add to your app's entry point:

// For debug builds only
if (__DEV__) {
  // XMLHttpRequest is used by fetch under the hood
  const originalXHR = global.XMLHttpRequest;
  // Use a network interceptor library or React Native DevTools
}

4.12 The New Architecture

React Native 0.86 defaults to the New Architecture, which consists of three pillars:

4.12.1 Fabric (New Renderer)

Fabric replaces the old UI manager with a C++ renderer that enables:

  • Synchronous layout and measurement
  • Concurrent rendering support
  • Better integration with platform views
  • Improved performance through shared ownership of UI trees

No action required for JavaScript-level code — Fabric is transparent to your components.

4.12.2 TurboModules

TurboModules replace the old bridge-based native modules with:

  • Lazy loading (modules loaded only when first accessed)
  • Direct JavaScript-to-native calls (no JSON serialization over the bridge)
  • Type-safe interfaces via Codegen

4.12.3 Codegen

Codegen generates type-safe C++ interfaces from your JavaScript/TypeScript/Flow type definitions. This ensures that JavaScript and native code always agree on the shape of data being passed.


5. Common Tasks

5.1 Storing Data Locally

5.1.1 AsyncStorage

npm install @react-native-async-storage/async-storage
import AsyncStorage from '@react-native-async-storage/async-storage';

// Store
await AsyncStorage.setItem('user_token', 'abc123');
await AsyncStorage.setItem('user_prefs', JSON.stringify({theme: 'dark'}));

// Retrieve
const token = await AsyncStorage.getItem('user_token');
const prefs = JSON.parse(await AsyncStorage.getItem('user_prefs') ?? '{}');

// Remove
await AsyncStorage.removeItem('user_token');

// Clear all
await AsyncStorage.clear();

// Multi operations
await AsyncStorage.multiSet([
  ['key1', 'value1'],
  ['key2', 'value2'],
]);

const values = await AsyncStorage.multiGet(['key1', 'key2']);
// values = [['key1', 'value1'], ['key2', 'value2']]

5.2 Handling Permissions

npm install react-native-permissions
import {check, request, PERMISSIONS, RESULTS} from 'react-native-permissions';
import {Platform} from 'react-native';

async function requestCameraPermission() {
  const permission = Platform.select({
    ios: PERMISSIONS.IOS.CAMERA,
    android: PERMISSIONS.ANDROID.CAMERA,
  });

  if (!permission) return;

  const result = await check(permission);

  switch (result) {
    case RESULTS.UNAVAILABLE:
      console.log('Feature not available on this device');
      break;
    case RESULTS.DENIED:
      const requestResult = await request(permission);
      console.log('Request result:', requestResult);
      break;
    case RESULTS.GRANTED:
      console.log('Permission already granted');
      break;
    case RESULTS.BLOCKED:
      console.log('Permission blocked — direct user to Settings');
      break;
  }
}
import {Linking} from 'react-native';

// Open a URL
await Linking.openURL('https://example.com');

// Open phone dialer
await Linking.openURL('tel:+15551234567');

// Open email
await Linking.openURL('mailto:[email removed]');

// Open app settings
await Linking.openSettings();

// Listen for incoming deep links
useEffect(() => {
  const handleDeepLink = ({url}: {url: string}) => {
    console.log('Deep link received:', url);
    // Parse and navigate
  };

  // Check if app was opened via deep link
  Linking.getInitialURL().then(url => {
    if (url) handleDeepLink({url});
  });

  // Listen for deep links while app is open
  const subscription = Linking.addEventListener('url', handleDeepLink);
  return () => subscription.remove();
}, []);

5.4 Clipboard Operations

import Clipboard from '@react-native-clipboard/clipboard';

// Copy to clipboard
Clipboard.setString('Text to copy');

// Read from clipboard
const text = await Clipboard.getString();

5.5 Using the Camera

npm install react-native-image-picker
import {launchCamera, launchImageLibrary} from 'react-native-image-picker';

// Take a photo
const result = await launchCamera({
  mediaType: 'photo',
  quality: 0.8,
  maxWidth: 1024,
  maxHeight: 1024,
});

if (result.assets?.[0]) {
  const photo = result.assets[0];
  console.log('URI:', photo.uri);
  console.log('Size:', photo.fileSize);
}

// Pick from gallery
const picked = await launchImageLibrary({
  mediaType: 'mixed',
  selectionLimit: 5,
});

5.6 Showing Alerts and Action Sheets

import {Alert, ActionSheetIOS, Platform} from 'react-native';

// Simple alert
Alert.alert('Title', 'Message');

// Alert with buttons
Alert.alert(
  'Delete Item',
  'Are you sure you want to delete this item?',
  [
    {text: 'Cancel', style: 'cancel'},
    {text: 'Delete', style: 'destructive', onPress: () => deleteItem()},
  ],
);

// Prompt for text input (iOS only)
Alert.prompt(
  'Enter Name',
  'Please enter your display name:',
  (text) => console.log('Entered:', text),
);

// Action Sheet (iOS)
if (Platform.OS === 'ios') {
  ActionSheetIOS.showActionSheetWithOptions(
    {
      options: ['Cancel', 'Share', 'Delete'],
      cancelButtonIndex: 0,
      destructiveButtonIndex: 2,
    },
    (buttonIndex) => {
      if (buttonIndex === 1) share();
      if (buttonIndex === 2) deleteItem();
    },
  );
}

5.7 Handling the Keyboard

import React from 'react';
import {
  KeyboardAvoidingView,
  Platform,
  TextInput,
  StyleSheet,
  View,
} from 'react-native';

function ChatInput(): React.JSX.Element {
  return (
    <KeyboardAvoidingView
      behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
      keyboardVerticalOffset={Platform.OS === 'ios' ? 88 : 0}
      style={styles.container}>
      <View style={styles.inputRow}>
        <TextInput style={styles.input} placeholder="Type a message..." />
      </View>
    </KeyboardAvoidingView>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  inputRow: {
    flexDirection: 'row',
    padding: 8,
    borderTopWidth: 1,
    borderTopColor: '#eee',
  },
  input: {
    flex: 1,
    borderWidth: 1,
    borderColor: '#ddd',
    borderRadius: 20,
    paddingHorizontal: 16,
    paddingVertical: 8,
  },
});

5.7.1 Keyboard Events

import {Keyboard} from 'react-native';

useEffect(() => {
  const showSub = Keyboard.addListener('keyboardDidShow', (event) => {
    console.log('Keyboard height:', event.endCoordinates.height);
  });
  const hideSub = Keyboard.addListener('keyboardDidHide', () => {
    console.log('Keyboard hidden');
  });

  return () => {
    showSub.remove();
    hideSub.remove();
  };
}, []);

// Dismiss keyboard programmatically
Keyboard.dismiss();

5.8 StatusBar Configuration

import {StatusBar} from 'react-native';

// Global configuration
<StatusBar
  barStyle="dark-content"     // 'dark-content' | 'light-content'
  backgroundColor="#ffffff"   // Android only
  translucent={false}         // Android only
  hidden={false}
/>

// Imperative API
StatusBar.setBarStyle('light-content');
StatusBar.setBackgroundColor('#000000'); // Android only
StatusBar.setHidden(true);

5.9 Sharing Content

import {Share} from 'react-native';

async function shareContent() {
  try {
    const result = await Share.share({
      title: 'Check this out',
      message: 'I found something interesting: https://example.com',
      url: 'https://example.com', // iOS only
    });

    if (result.action === Share.sharedAction) {
      console.log('Shared successfully');
    } else if (result.action === Share.dismissedAction) {
      console.log('Share dismissed');
    }
  } catch (error) {
    console.error('Share error:', error);
  }
}

5.10 App State Handling

import {AppState} from 'react-native';

useEffect(() => {
  const subscription = AppState.addEventListener('change', (nextAppState) => {
    if (nextAppState === 'active') {
      console.log('App came to foreground');
      // Refresh data, reconnect sockets, etc.
    } else if (nextAppState === 'background') {
      console.log('App went to background');
      // Save state, pause tasks, etc.
    } else if (nextAppState === 'inactive') {
      console.log('App is transitioning (iOS only)');
    }
  });

  return () => subscription.remove();
}, []);

5.11 Dimensions and Window Size

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

// Hook (recommended — updates on rotation)
function ResponsiveLayout(): React.JSX.Element {
  const {width, height} = useWindowDimensions();
  const isLandscape = width > height;

  return (
    <View style={{flexDirection: isLandscape ? 'row' : 'column'}}>
      {/* Responsive layout */}
    </View>
  );
}

// Static API (doesn't update on rotation)
const {width, height} = Dimensions.get('window');
const screenDimensions = Dimensions.get('screen'); // Includes status bar

5.12 Accessibility

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

function AccessibleButton(): React.JSX.Element {
  return (
    <TouchableOpacity
      accessible={true}
      accessibilityLabel="Delete this item"
      accessibilityHint="Double tap to permanently remove this item"
      accessibilityRole="button"
      accessibilityState={{disabled: false}}
      onPress={handleDelete}>
      <Text>Delete</Text>
    </TouchableOpacity>
  );
}

// Live region (announces changes to screen reader)
<Text accessibilityLiveRegion="polite">
  {itemCount} items remaining
</Text>

// Accessibility roles
// 'button', 'link', 'header', 'search', 'image', 'text',
// 'adjustable', 'alert', 'checkbox', 'radio', 'switch', 'tab', etc.

5.13 Timers and Intervals

import React, {useEffect, useRef} from 'react';

function useInterval(callback: () => void, delay: number | null) {
  const savedCallback = useRef(callback);

  useEffect(() => {
    savedCallback.current = callback;
  }, [callback]);

  useEffect(() => {
    if (delay === null) return;
    const id = setInterval(() => savedCallback.current(), delay);
    return () => clearInterval(id);
  }, [delay]);
}

// Usage
function Countdown(): React.JSX.Element {
  const [seconds, setSeconds] = useState(60);

  useInterval(() => {
    setSeconds(s => s - 1);
  }, seconds > 0 ? 1000 : null);

  return <Text>{seconds}s remaining</Text>;
}

5.14 Modal Dialogs

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

function ModalExample(): React.JSX.Element {
  const [visible, setVisible] = useState(false);

  return (
    <>
      <Pressable onPress={() => setVisible(true)}>
        <Text>Open Modal</Text>
      </Pressable>

      <Modal
        animationType="slide"
        transparent={true}
        visible={visible}
        onRequestClose={() => setVisible(false)}>
        <View style={styles.modalOverlay}>
          <View style={styles.modalContent}>
            <Text style={styles.modalTitle}>Modal Title</Text>
            <Text>This is a modal dialog.</Text>
            <Pressable
              style={styles.closeButton}
              onPress={() => setVisible(false)}>
              <Text style={styles.closeText}>Close</Text>
            </Pressable>
          </View>
        </View>
      </Modal>
    </>
  );
}

const styles = StyleSheet.create({
  modalOverlay: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: 'rgba(0,0,0,0.5)',
  },
  modalContent: {
    backgroundColor: 'white',
    borderRadius: 16,
    padding: 24,
    width: '80%',
    alignItems: 'center',
  },
  modalTitle: {
    fontSize: 20,
    fontWeight: 'bold',
    marginBottom: 12,
  },
  closeButton: {
    marginTop: 16,
    paddingVertical: 8,
    paddingHorizontal: 24,
    backgroundColor: '#3498db',
    borderRadius: 8,
  },
  closeText: {
    color: 'white',
    fontWeight: '600',
  },
});

5.15 Appearance and Dark Mode

import {useColorScheme, Appearance} from 'react-native';

function ThemedApp(): React.JSX.Element {
  const colorScheme = useColorScheme(); // 'light' | 'dark' | null

  const theme = {
    background: colorScheme === 'dark' ? '#1a1a1a' : '#ffffff',
    text: colorScheme === 'dark' ? '#ffffff' : '#1a1a1a',
    card: colorScheme === 'dark' ? '#2a2a2a' : '#f5f5f5',
  };

  return (
    <View style={{flex: 1, backgroundColor: theme.background}}>
      <Text style={{color: theme.text}}>
        Current scheme: {colorScheme}
      </Text>
    </View>
  );
}

// Imperative API
const current = Appearance.getColorScheme();
const listener = Appearance.addChangeListener(({colorScheme}) => {
  console.log('Theme changed to:', colorScheme);
});

5.16 Building Production Apps

5.16.1 Bundle for Production

# Create the JavaScript bundle
npx react-native bundle \
  --platform ios \
  --dev false \
  --entry-file index.js \
  --bundle-output ios/main.jsbundle \
  --assets-dest ios

npx react-native bundle \
  --platform android \
  --dev false \
  --entry-file index.js \
  --bundle-output android/app/src/main/assets/index.android.bundle \
  --assets-dest android/app/src/main/res

5.16.2 Build Android Release APK/AAB

cd android
./gradlew assembleRelease    # APK
./gradlew bundleRelease      # AAB (recommended for Play Store)

Output locations:

android/app/build/outputs/apk/release/app-release.apk
android/app/build/outputs/bundle/release/app-release.aab

Important

Release builds require a signing key. Configure signing in android/app/build.gradle under signingConfigs before building. Without proper signing, the app will not install on devices or be accepted by the Play Store.

5.16.3 Build iOS Release

  1. Open ios/MyProject.xcworkspace in Xcode
  2. Select "Any iOS Device" as the build target
  3. Product > Archive
  4. In the Organizer window, select the archive and click "Distribute App"

5.17 Environment Variables

# Set during Metro start
RCT_METRO_PORT=8088 npx react-native start

# Check dev mode in code
if (__DEV__) {
  console.log('Running in development mode');
}

For more comprehensive environment variable management, use a library like react-native-config:

npm install react-native-config
# .env
API_URL=https://api.example.com
API_KEY=your_api_key
import Config from 'react-native-config';

console.log(Config.API_URL); // 'https://api.example.com'

5.18 Custom Fonts

5.18.1 Using Static Assets

  1. Place font files in assets/fonts/ directory
  2. Add to react-native.config.js:
module.exports = {
  project: {
    ios: {},
    android: {},
  },
  assets: ['./assets/fonts/'],
};
  1. Link the fonts:
npx react-native-asset
  1. Use in styles:
const styles = StyleSheet.create({
  text: {
    fontFamily: 'Montserrat-Bold', // Exact filename without extension
  },
});

5.19 Error Boundaries

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

interface ErrorBoundaryState {
  hasError: boolean;
  error: Error | null;
}

class ErrorBoundary extends React.Component<
  {children: React.ReactNode},
  ErrorBoundaryState
> {
  constructor(props: {children: React.ReactNode}) {
    super(props);
    this.state = {hasError: false, error: null};
  }

  static getDerivedStateFromError(error: Error) {
    return {hasError: true, error};
  }

  componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
    // Log to crash reporting service
    console.error('ErrorBoundary caught:', error, errorInfo);
  }

  render() {
    if (this.state.hasError) {
      return (
        <View style={styles.container}>
          <Text style={styles.title}>Something went wrong</Text>
          <Text style={styles.message}>{this.state.error?.message}</Text>
          <Button
            title="Try Again"
            onPress={() => this.setState({hasError: false, error: null})}
          />
        </View>
      );
    }
    return this.props.children;
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    padding: 20,
  },
  title: {
    fontSize: 20,
    fontWeight: 'bold',
    marginBottom: 8,
  },
  message: {
    color: '#666',
    marginBottom: 16,
    textAlign: 'center',
  },
});

5.20 Performance Optimization

5.20.1 Memoization

import React, {useMemo, useCallback} from 'react';

// Memoize expensive computations
const sortedData = useMemo(() => {
  return data.sort((a, b) => a.name.localeCompare(b.name));
}, [data]);

// Memoize callbacks
const handlePress = useCallback((id: string) => {
  setSelectedId(id);
}, []);

// Memoize components
const MemoizedItem = React.memo(function Item({title, onPress}: ItemProps) {
  return (
    <Pressable onPress={onPress}>
      <Text>{title}</Text>
    </Pressable>
  );
});

5.20.2 FlatList Optimization

<FlatList
  data={data}
  renderItem={renderItem}
  keyExtractor={keyExtractor}
  // Performance props
  removeClippedSubviews={true}       // Unmount off-screen views
  maxToRenderPerBatch={10}           // Items per render batch
  updateCellsBatchingPeriod={50}     // Batch update interval (ms)
  windowSize={5}                     // Render window multiplier
  initialNumToRender={10}            // Initial render count
  getItemLayout={(data, index) => ({ // Skip measurement
    length: ITEM_HEIGHT,
    offset: ITEM_HEIGHT * index,
    index,
  })}
/>

5.20.3 Image Optimization

// Use FastImage for better caching (community library)
import FastImage from 'react-native-fast-image';

<FastImage
  style={{width: 200, height: 200}}
  source={{
    uri: 'https://example.com/photo.jpg',
    priority: FastImage.priority.normal,
    cache: FastImage.cacheControl.immutable,
  }}
  resizeMode={FastImage.resizeMode.cover}
/>

5.21 Testing

5.21.1 Unit Tests with Jest

// __tests__/utils.test.ts
import {formatCurrency, validateEmail} from '../src/utils';

describe('formatCurrency', () => {
  it('formats dollars correctly', () => {
    expect(formatCurrency(1234.5)).toBe('$1,234.50');
  });

  it('handles zero', () => {
    expect(formatCurrency(0)).toBe('$0.00');
  });
});

describe('validateEmail', () => {
  it('accepts valid emails', () => {
    expect(validateEmail('[email removed]')).toBe(true);
  });

  it('rejects invalid emails', () => {
    expect(validateEmail('not-an-email')).toBe(false);
  });
});

Run tests:

npx jest

# With coverage
npx jest --coverage

# Watch mode
npx jest --watch

5.21.2 Component Tests

// __tests__/Counter.test.tsx
import React from 'react';
import {render, fireEvent} from '@testing-library/react-native';
import Counter from '../src/Counter';

describe('Counter', () => {
  it('renders initial count', () => {
    const {getByText} = render(<Counter />);
    expect(getByText('Count: 0')).toBeTruthy();
  });

  it('increments on press', () => {
    const {getByText} = render(<Counter />);
    fireEvent.press(getByText('Increment'));
    expect(getByText('Count: 1')).toBeTruthy();
  });
});

5.21.3 Snapshot Tests

import React from 'react';
import renderer from 'react-test-renderer';
import App from '../App';

it('renders correctly', () => {
  const tree = renderer.create(<App />).toJSON();
  expect(tree).toMatchSnapshot();
});

5.22 Upgrading React Native

# Check available upgrade
npx react-native upgrade

# Or use the upgrade helper website to see diffs between versions:
# https://react-native-community.github.io/upgrade-helper/

Caution

Major version upgrades can involve breaking changes to native code, Gradle configurations, Podfile settings, and third-party library compatibility. Always read the release notes, back up your project, and test thoroughly on both platforms after upgrading.

5.23 Using Native Modules (TurboModules)

With the New Architecture, native modules use Codegen for type-safe bridging.

5.23.1 Defining a TurboModule Spec (TypeScript)

// NativeCalculator.ts
import type {TurboModule} from 'react-native';
import {TurboModuleRegistry} from 'react-native';

export interface Spec extends TurboModule {
  add(a: number, b: number): number;
  multiply(a: number, b: number): Promise<number>;
}

export default TurboModuleRegistry.getEnforcing<Spec>('Calculator');

5.23.2 Using the Module

import NativeCalculator from './NativeCalculator';

const sum = NativeCalculator.add(3, 4); // Synchronous: 7
const product = await NativeCalculator.multiply(3, 4); // Async: 12

5.24 Handling Back Button (Android)

import {BackHandler} from 'react-native';

useEffect(() => {
  const backAction = () => {
    if (canGoBack) {
      goBack();
      return true; // Prevent default behavior
    }
    return false; // Let system handle it
  };

  const backHandler = BackHandler.addEventListener(
    'hardwareBackPress',
    backAction,
  );

  return () => backHandler.remove();
}, [canGoBack]);

5.25 Vibration and Haptic Feedback

import {Vibration} from 'react-native';

// Simple vibration
Vibration.vibrate();

// Duration (Android only; iOS ignores the duration)
Vibration.vibrate(500);

// Pattern: [wait, vibrate, wait, vibrate, ...]
Vibration.vibrate([500, 1000, 500, 2000]);

// Cancel
Vibration.cancel();

5.26 Using the PixelRatio API

import {PixelRatio} from 'react-native';

// Get device pixel density
const density = PixelRatio.get(); // e.g., 2 for @2x, 3 for @3x

// Convert layout size to pixel size
const pixelSize = PixelRatio.getPixelSizeForLayoutSize(100);

// Round to nearest pixel
const rounded = PixelRatio.roundToNearestPixel(8.4);

// Get font scale
const fontScale = PixelRatio.getFontScale();

6. Troubleshooting

6.1 Build and Setup Issues

Error: "Unable to load script from assets 'index.android.bundle'"

Cause: The JavaScript bundle is not being served to the Android emulator or device.

Solution:

  1. Ensure Metro is running: npx react-native start
  2. If using a physical device, ensure it's on the same network as your development machine
  3. For the emulator, reverse the port:
    adb reverse tcp:8081 tcp:8081
    
  4. If the port was changed, use the correct port:
    adb reverse tcp:8088 tcp:8088
    

Prevention: Always start Metro before running the app. If using a custom port via RCT_METRO_PORT, ensure it matches everywhere.

Error: "Build failed" with CocoaPods issues

Cause: Stale or corrupt CocoaPods cache, or mismatched dependency versions.

Solution:

  1. Clean and reinstall:
    cd ios
    rm -rf Pods Podfile.lock
    pod cache clean --all
    pod install
    cd ..
    
  2. If that fails, also clean the build folder:
    cd ios
    xcodebuild clean
    cd ..
    

Prevention: Run pod install after every npm install that adds native dependencies.

Error: "Inconsistent Podfile.lock checksums for hermes-engine"

Cause: The Podfile.lock has cached checksums that don't match the current Hermes binary. This is a known issue (GitHub #54891) that can occur after upgrading React Native or switching branches.

Solution:

  1. Delete the lockfile and reinstall:
    cd ios
    rm Podfile.lock
    pod install
    cd ..
    
  2. If the error persists, clean the CocoaPods cache for Hermes specifically:
    pod cache clean hermes-engine
    pod install
    

Prevention: Always delete Podfile.lock before running pod install after a React Native version upgrade.

Error: "SDK location not found"

Cause: ANDROID_HOME environment variable is not set or points to the wrong location.

Solution:

  1. Find your Android SDK location (check Android Studio > Settings > SDK Manager)
  2. Set the environment variable:
    export ANDROID_HOME=$HOME/Library/Android/sdk  # macOS
    export ANDROID_HOME=$HOME/Android/Sdk           # Linux
    
  3. Add to your shell profile (~/.zshrc, ~/.bashrc)

Prevention: Add ANDROID_HOME to your shell profile during initial setup.

Error: "foojay-resolver-convention 0.5.0 incompatible with Gradle 9.0.0"

Cause: The foojay-resolver-convention plugin version bundled with React Native is too old for Gradle 9.x. This is a known issue (GitHub #56287).

Solution:

  1. In android/settings.gradle, update the plugin version:
    plugins {
        id 'org.gradle.toolchains.foojay-resolver-convention' version '0.9.0'
    }
    
  2. Or downgrade to a compatible Gradle version in android/gradle/wrapper/gradle-wrapper.properties:
    distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-all.zip
    

Prevention: Check Gradle compatibility before upgrading Android build tools.

6.2 Runtime Errors

Error: "Invariant Violation: requireNativeComponent: 'RCTView' was not found"

Cause: Native components are not properly linked, typically after adding a library with native code.

Solution:

  1. For iOS:
    cd ios && pod install && cd ..
    npx react-native run-ios
    
  2. For Android, rebuild:
    cd android && ./gradlew clean && cd ..
    npx react-native run-android
    

Prevention: Always run pod install (iOS) and rebuild after adding libraries with native dependencies.

Error: "VirtualizedLists should never be nested inside plain ScrollViews"

Cause: A FlatList or SectionList is placed inside a ScrollView, creating competing scroll containers.

Solution:

  1. Replace the outer ScrollView with a View
  2. Or use FlatList's ListHeaderComponent and ListFooterComponent for content above/below the list
  3. Or use the nestedScrollEnabled prop (Android only, not recommended)
// Instead of this:
<ScrollView>
  <Header />
  <FlatList data={data} renderItem={renderItem} />
</ScrollView>

// Do this:
<FlatList
  data={data}
  renderItem={renderItem}
  ListHeaderComponent={<Header />}
/>

Prevention: Design layouts so FlatList/SectionList is the outermost scrolling container.

Error: "Animations do not run correctly in React Navigation"

Cause: A known issue (GitHub #56347) where Animated animations conflict with React Navigation's transition animations on iOS, particularly when using Animated.timing inside screen components.

Solution:

  1. Use useNativeDriver: true in all animations
  2. Defer animations until after the navigation transition completes:
    useFocusEffect(
      useCallback(() => {
        // Start animations here, after screen is focused
        Animated.timing(opacity, {
          toValue: 1,
          duration: 300,
          useNativeDriver: true,
        }).start();
      }, []),
    );
    
  3. Consider using react-native-reanimated for more complex animation scenarios

Prevention: Always use useNativeDriver: true and tie animations to navigation lifecycle events.

Error: "Network request failed"

Cause: This intermittent error (GitHub #56480) can stem from multiple causes: network timeout, SSL certificate issues, or Android cleartext traffic restrictions.

Solution:

  1. On Android, ensure cleartext traffic is allowed for development:
    <!-- android/app/src/main/AndroidManifest.xml -->
    <application android:usesCleartextTraffic="true" ...>
    
  2. Check network connectivity
  3. Add error retry logic:
    async function fetchWithRetry(url: string, retries = 3) {
      for (let i = 0; i < retries; i++) {
        try {
          const response = await fetch(url);
          return response;
        } catch (error) {
          if (i === retries - 1) throw error;
          await new Promise(r => setTimeout(r, 1000 * (i + 1)));
        }
      }
    }
    

Prevention: Implement retry logic and timeout handling for all network requests.

Error: App crashes on startup with "TurboModule performVoidMethodInvocation - SIGABRT" (iOS)

Cause: A known issue on iOS 26 (GitHub #54859) where TurboModule initialization triggers a crash during app startup. This affects apps using the New Architecture.

Solution:

  1. Check if you're running on the latest React Native patch version — this may have a fix
  2. If on an older version, try disabling specific TurboModules that may be causing the crash
  3. Check the GitHub issue for the latest workarounds specific to your iOS version

Prevention: Test on the latest iOS betas early and track known issues in the React Native repository.

Error: "EXC_BAD_ACCESS in production after upgrading"

Cause: Memory corruption, often related to the Fabric renderer or native module lifecycle changes between React Native versions (GitHub #53774).

Solution:

  1. Clean all build caches:
    cd ios
    rm -rf Pods Podfile.lock build DerivedData
    pod install
    cd ..
    npx react-native run-ios --mode Release
    
  2. If persists, check for native modules that haven't been updated for the current React Native version
  3. Test with native modules removed one at a time to isolate the cause

Prevention: After upgrading React Native, test release builds on physical devices before deploying to users.

6.3 UI and Layout Issues

Visual jump when using RefreshControl

Cause: A longstanding issue (GitHub #31461) where RefreshControl causes the list to jump to an unexpected scroll position when the refresh animation begins.

Solution:

  1. Set progressViewOffset on Android to adjust the refresh indicator position:
    <FlatList
      refreshControl={
        <RefreshControl
          refreshing={refreshing}
          onRefresh={onRefresh}
          progressViewOffset={Platform.OS === 'android' ? 50 : 0}
        />
      }
    />
    
  2. On iOS, wrapping in a fixed-height container can help mitigate the jump

Prevention: Test pull-to-refresh behavior across both platforms during development.

Nested Text not focusable by hardware keyboard

Cause: A known issue (GitHub #35194) where nested <Text> components inside tappable elements cannot receive focus from hardware keyboards or accessibility devices.

Solution:

  1. Use accessible={true} on the outermost tappable container
  2. Set accessibilityRole="link" or accessibilityRole="button" on the container
  3. For keyboard navigation, consider wrapping tappable text in a Pressable:
    <Pressable onPress={handlePress} accessible accessibilityRole="link">
      <Text>
        Read the <Text style={{color: 'blue'}}>documentation</Text>
      </Text>
    </Pressable>
    

Prevention: Always test with hardware keyboards and screen readers.

Nested View and Text vertical alignment issue

Cause: When mixing View and Text components in a row layout, vertical alignment can be inconsistent because Text components use text baseline alignment while View components use their box model (GitHub #31955).

Solution:

  1. Use alignItems: 'center' on the parent flex row
  2. Ensure the View and Text have explicit matching heights
  3. Apply textAlignVertical: 'center' (Android) to Text components

Prevention: Use consistent component types in flex rows. Wrap mixed content in View containers with explicit alignment.

letterSpacing not working properly

Cause: A known issue (GitHub #54823) where letterSpacing behaves inconsistently across platforms, particularly with certain font families and sizes.

Solution:

  1. Test letterSpacing values on both platforms
  2. Use platform-specific values if needed:
    const styles = StyleSheet.create({
      text: {
        letterSpacing: Platform.select({ios: 1.5, android: 2}),
      },
    });
    

Prevention: Always verify text styling on both iOS and Android.

KeyboardAvoidingView not keeping input visible (Android 15)

Cause: On Android 15, the keyboard insets reported to KeyboardAvoidingView can be incorrect, causing the input field to remain hidden behind the keyboard (GitHub #49759).

Solution:

  1. Use behavior="height" instead of behavior="padding" on Android:
    <KeyboardAvoidingView
      behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
      keyboardVerticalOffset={Platform.select({ios: 88, android: 0})}>
      {/* Input fields */}
    </KeyboardAvoidingView>
    
  2. Alternatively, use android:windowSoftInputMode="adjustResize" in AndroidManifest.xml
  3. Consider react-native-keyboard-aware-scroll-view as a community alternative

Prevention: Test keyboard behavior on the latest Android versions during development.

getStatusBarHeightPx initialized before DecorView attached

Cause: On some Android devices, StatusBar height methods are called before the view hierarchy is ready (GitHub #53251), returning 0 or incorrect values.

Solution:

  1. Defer status bar height reads to after onLayout:
    <View
      onLayout={() => {
        // StatusBar height is now reliable
        const height = StatusBar.currentHeight;
      }}
    />
    
  2. Use react-native-safe-area-context which handles this timing correctly

Prevention: Avoid reading StatusBar.currentHeight in component constructors or early lifecycle methods.

6.4 Development Server Issues

Error: "Could not connect to development server"

Cause: Metro is not running, or the device cannot reach the dev server.

Solution:

  1. Start Metro: npx react-native start
  2. On Android emulator, set up port forwarding:
    adb reverse tcp:8081 tcp:8081
    
  3. On physical device, ensure same Wi-Fi network and correct IP
  4. Check if another process is using port 8081:
    lsof -i :8081
    
  5. Use a different port:
    RCT_METRO_PORT=8088 npx react-native start
    

Prevention: Kill stale Metro processes before starting a new one.

Error: "EMFILE: too many open files"

Cause: Metro's file watcher exceeds the OS file descriptor limit.

Solution:

  1. Install Watchman (recommended for all platforms):
    brew install watchman   # macOS
    
  2. On Linux, increase the limit:
    echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf
    sudo sysctl -p
    

Prevention: Install Watchman as part of initial setup.

Metro bundler stuck or stale cache

Cause: Metro's cache can become corrupted after branch switches, dependency changes, or interrupted builds.

Solution:

  1. Start Metro with a clean cache:
    npx react-native start --reset-cache
    
  2. Delete the Metro cache directory manually:
    rm -rf $TMPDIR/metro-*
    rm -rf $TMPDIR/haste-map-*
    

Prevention: Use --reset-cache after major changes like upgrading React Native or switching branches with different dependency trees.

6.5 Android-Specific Issues

Broken code completion / IntelliSense in Android Studio

Cause: A known issue (GitHub #55517) where Android Studio's code completion fails for React Native projects due to Gradle sync or indexing issues with the React Native Gradle plugin.

Solution:

  1. Invalidate caches and restart: File > Invalidate Caches / Restart
  2. Re-sync Gradle: File > Sync Project with Gradle Files
  3. Ensure Android Studio is updated to the latest stable version

Prevention: Keep Android Studio and the Android Gradle plugin at compatible versions.

Hermes profiling: heap snapshot fails with OkHttp WebSocket overflow

Cause: When taking heap snapshots via Hermes CDP, OkHttp's WebSocket queue can overflow (GitHub #56470), causing the connection to close before the snapshot completes.

Solution:

  1. Use React Native DevTools instead of Chrome DevTools for heap profiling
  2. Reduce memory pressure before taking snapshots (close other apps, reduce data in state)
  3. If using a custom CDP client, increase WebSocket buffer sizes

Prevention: Use the built-in React Native DevTools for Hermes debugging tasks.

HermesSamplingProfiler JNI method mismatch

Cause: A known issue (GitHub #56126) where the JNI "disable" method is incorrectly mapped to the native enable function, potentially causing profiling to behave unexpectedly on Android.

Solution:

  1. Check if a patch is available for your React Native version
  2. Avoid using the Hermes sampling profiler until the fix is released
  3. Use the JavaScript profiler in React Native DevTools as an alternative

Prevention: Monitor the GitHub issue for resolution in upcoming releases.

6.6 Performance Issues

App feels slow / janky scrolling

Cause: Heavy computation on the JavaScript thread blocks the UI.

Solution:

  1. Move to native driver for animations:
    Animated.timing(value, {
      toValue: 1,
      useNativeDriver: true, // Key for performance
    }).start();
    
  2. Optimize FlatList (see section 5.20.2)
  3. Use React.memo for list items
  4. Avoid inline functions in renderItem:
    // Bad — creates new function every render
    renderItem={({item}) => <Item data={item} onPress={() => select(item.id)} />}
    
    // Good — stable reference
    const renderItem = useCallback(({item}) => (
      <Item data={item} onPress={handlePress} />
    ), [handlePress]);
    

Prevention: Profile regularly with the React DevTools Profiler and Systrace.

Large bundle size

Cause: Unused code, large assets, or unoptimized dependencies.

Solution:

  1. Enable inline requires (already default in Metro config: inlineRequires: true)
  2. Analyze bundle size:
    npx react-native bundle \
      --platform android \
      --dev false \
      --entry-file index.js \
      --bundle-output /tmp/bundle.js \
      --sourcemap-output /tmp/bundle.map
    
    npx source-map-explorer /tmp/bundle.js /tmp/bundle.map
    
  3. Remove unused dependencies from package.json
  4. Use dynamic imports for rarely-used features

Prevention: Monitor bundle size as part of your CI/CD pipeline.

6.7 General Debugging Strategies

Full cache reset (nuclear option)

When nothing else works, clear everything:

# Stop Metro
# Kill all running emulators/simulators

# Clear JS/Node caches
rm -rf node_modules
npm cache clean --force
npm install

# Clear Metro cache
rm -rf $TMPDIR/metro-*
rm -rf $TMPDIR/haste-map-*

# Clear iOS caches
cd ios
rm -rf Pods Podfile.lock build
pod cache clean --all
pod install
cd ..

# Clear Android caches
cd android
./gradlew clean
cd ..

# Clear Xcode DerivedData
rm -rf ~/Library/Developer/Xcode/DerivedData

# Restart Metro with clean cache
npx react-native start --reset-cache

Warning

The full cache reset deletes significant build artifacts and caches. Expect longer build times for the next build as everything is regenerated from scratch. Only use this when targeted solutions have failed.


7. FAQ

Q: What JavaScript engine does React Native use?

React Native uses Hermes as its default JavaScript engine. Hermes is an open-source engine optimized for React Native with ahead-of-time compilation, lower memory usage, and faster startup compared to JavaScriptCore. It is built in and enabled by default.

Q: Can I use TypeScript with React Native?

Yes. New projects created with npx @react-native-community/cli@latest init include TypeScript support out of the box, with a tsconfig.json that extends @react-native/typescript-config. You can write all your components in .tsx files.

Q: What is the New Architecture?

The New Architecture is the modernized internals of React Native, consisting of three components: Fabric (the new renderer for UI), TurboModules (the new native module system with lazy loading and direct calls), and Codegen (type-safe bridge code generation). It is enabled by default in React Native 0.86.

Q: Can I build for both iOS and Android from a single codebase?

Yes, that is React Native's primary purpose. You write your UI components and business logic once in JavaScript/TypeScript, and React Native renders native views on each platform. Platform-specific code can be handled via Platform.OS checks, Platform.select(), or platform-specific file extensions (.ios.tsx, .android.tsx).

Q: Can I use npm packages from the web ecosystem?

Many npm packages work in React Native, particularly those that are pure JavaScript. Packages that depend on browser-specific APIs (DOM, window, document, Canvas, etc.) will not work. Always check a package's React Native compatibility before installing.

Q: How do I handle different screen sizes?

Use Flexbox for responsive layouts, useWindowDimensions() for dimension-aware rendering, percentage-based widths/heights, and PixelRatio for pixel-perfect designs. React Native's Flexbox implementation handles density-independent pixels automatically.

Q: What is Metro?

Metro is React Native's JavaScript bundler. It watches your source files, transforms them through Babel, resolves dependencies, and serves the bundle to your app. It runs on port 8081 by default (configurable via RCT_METRO_PORT). The default configuration resolves modules using the field order ['react-native', 'browser', 'main'] and supports the platforms ['android', 'ios'].

Q: How do I add custom native code?

For the New Architecture, create a TurboModule specification in TypeScript/Flow, then implement the native side in Swift/Objective-C (iOS) or Kotlin/Java (Android). Codegen generates the C++ bridge code. For Fabric components, define a component spec and implement the native view. See the Admin Guide for architecture details.

Q: What is Fast Refresh?

Fast Refresh is React Native's hot reloading system. When you save a file, Metro sends only the changed module to the running app, which updates the component tree while preserving state. It handles both functional and class components, and falls back to a full reload when structural changes require it.

Q: How do I debug React Native apps?

React Native includes a built-in debugger based on Chrome DevTools (@react-native/debugger-frontend). Access it via the Dev Menu (shake device, Cmd+D, or Cmd+M). You can also use npx react-devtools for component tree inspection, or the inspector proxy endpoints (/json, /json/list, /json/version) for custom debugger integrations.

Q: Do I need a Mac to build for iOS?

Yes. iOS apps can only be built on macOS with Xcode installed. There is no way around this Apple platform requirement. For teams without Mac hardware, cloud Mac services (like Mac Stadium or GitHub Actions macOS runners) are common solutions.

Q: How do I handle navigation between screens?

React Native does not include a built-in navigation library. The community standard is React Navigation (@react-navigation), which provides stack, tab, drawer, and other navigation patterns. See section 4.5 for setup and usage examples.

Q: Can I use React Native with Expo?

Yes. Expo is a popular framework built on top of React Native that provides additional tooling, pre-configured native modules, and a managed workflow. This guide focuses on the bare React Native CLI workflow, but many concepts apply to both.

Q: How do I handle images with different resolutions?

For local images, provide @1x, @2x, and @3x variants:

assets/
  logo.png       (100x100)
  logo@2x.png    (200x200)
  logo@3x.png    (300x300)

React Native's asset system (backed by @react-native/assets-registry) automatically selects the correct variant for the device's pixel density.

Q: What is the difference between ScrollView and FlatList?

ScrollView renders all children at once — suitable for small, fixed-size content. FlatList virtualizes its children, only rendering items visible on screen — required for large or dynamic lists. Use FlatList (or SectionList) whenever your list has more than a few dozen items.

Q: How do I handle forms and validation?

React Native provides TextInput for form fields. For form management and validation, community libraries like Formik or React Hook Form work well with React Native. These are standard React libraries that are fully compatible.

Q: How do I add splash screens?

React Native does not include built-in splash screen support. Use the community library react-native-splash-screen or react-native-bootsplash. Configure the native splash screen in Xcode (LaunchScreen.storyboard) and Android (styles.xml theme).

Q: Can I use web CSS in React Native?

No. React Native uses a JavaScript-based styling system that resembles CSS but has key differences: all properties are camelCase, dimensions are unitless (density-independent pixels), flexDirection defaults to column, there is no cascading or inheritance (except within Text), and not all CSS properties are supported. Use StyleSheet.create() for all styles.

Q: How do I handle offline/no-network scenarios?

Use the NetInfo community library (@react-native-community/netinfo) to detect connectivity status. Combine with local storage (AsyncStorage or SQLite) to cache data for offline use. Implement retry queues for failed network requests.

Q: What are the supported platforms?

React Native officially supports iOS and Android. The Metro configuration defaults to platforms: ['android', 'ios']. Community-driven efforts extend support to Windows, macOS, web, and other targets via out-of-tree platforms.

Q: How do I handle app permissions?

Use the react-native-permissions community library for a cross-platform permissions API. On iOS, configure required permission descriptions in Info.plist. On Android, declare permissions in AndroidManifest.xml. See section 5.2 for examples.

Q: Why is my app's startup time slow?

Common causes: large JavaScript bundle, too many modules loaded at startup, heavy initial rendering. Solutions: enable Hermes (default), use inline requires (enabled by default in Metro), lazy-load screens, minimize initial render tree, and profile with Hermes bytecode compilation stats.

Q: How do I use environment variables?

React Native provides __DEV__ globally to check if you're in development mode. For custom environment variables, use the react-native-config library or set them via RCT_METRO_PORT and similar env vars at build time. Do not hardcode secrets in your JavaScript bundle.

Q: Can I share code between React Native and React web?

Yes, with caveats. Business logic, hooks, state management, and API calls can be shared. UI components cannot be shared directly because React Native uses View/Text instead of div/span. Libraries like React Native Web bridge this gap by mapping React Native components to web equivalents.

Q: How do I profile my app's performance?

Use the React DevTools Profiler for component render profiling. Use Systrace for native-level profiling. On Android, use Android Studio's profiler. On iOS, use Xcode Instruments. The Hermes profiler provides JavaScript-specific profiling data.

Q: What is the __DEV__ variable?

__DEV__ is a global boolean set to true in development builds and false in production builds. Use it to conditionally include debug logging, development tools, or mock data:

if (__DEV__) {
  console.log('Debug info:', data);
}

Q: How do I handle the Android hardware back button?

Use BackHandler from React Native (see section 5.24). Return true from the handler to prevent the default back behavior, or false to allow it. React Navigation handles back button integration automatically for its navigation stacks.