Paramclasses Academy - Blog

Tutorials Details

←
Post 34 of 39
→

How to Improve React Native App Performance and Loading Speed ?

How to Improve React Native App Performance and Loading Speed ?

November 5, 2026

1. Introduction

Application performance is one of the most important factors in mobile app development.

A slow application can create a poor user experience. Users expect mobile applications to open quickly, respond immediately, scroll smoothly, and display content without unnecessary delays.

React Native provides several tools and techniques to improve application performance.

In this tutorial, we will learn how to optimize React Native applications using practical examples.

What You Will Learn

  • What is application performance?

  • Why React Native apps become slow

  • How to reduce unnecessary re-renders

  • How to use React.memo()

  • How to use useMemo()

  • How to use useCallback()

  • How to optimize FlatList

  • How to optimize images

  • How to reduce application loading time

  • How to improve scrolling performance

  • How to use React Native DevTools

  • How to measure application performance

  • Performance optimization best practices

2. What Is App Performance?

App performance refers to how efficiently an application uses device resources and responds to user interactions.

A high-performance application should:

  • Open quickly.

  • Respond smoothly to button presses.

  • Scroll large lists without lag.

  • Display images efficiently.

  • Avoid unnecessary component rendering.

  • Use memory and CPU resources efficiently.

A smooth user interface generally targets 60 frames per second or higher when supported by the device and rendering configuration.

3. Common Reasons for Slow React Native Apps

Some common performance problems include:

  1. Rendering too many components unnecessarily.

  2. Using ScrollView for very large lists.

  3. Loading unnecessarily large images.

  4. Performing expensive calculations during every render.

  5. Running heavy JavaScript operations on the main JavaScript execution thread.

  6. Keeping unnecessary data in application state.

  7. Using complex components inside FlatList.

  8. Testing performance only in development mode.

  9. Keeping excessive console.log statements in production.

  10. Loading too much data when an application starts.

Understanding these problems helps us choose the correct optimization technique.

4. Understand React Native Rendering

When a component's state or props change, React may render that component again.

For example:

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

export default function App() {

  const [count, setCount] = useState(0);

  return (
    <View>

      <Text>Counter: {count}</Text>

      <Button
        title="Increase"
        onPress={() => setCount(count + 1)}
      />

    </View>
  );
}

Every time the button is pressed, the state changes and React updates the relevant UI.

This is normal behavior.

However, unnecessary rendering of expensive child components can affect performance.

5. Optimize Components Using React.memo()

React.memo() is a higher-order component that can skip rendering a component when its props have not changed.

Example Without React.memo()

function StudentCard({ name }) {

  console.log('StudentCard rendered');

  return (
    <Text>{name}</Text>
  );
}

If the parent component renders again, this child can also render again.

Example With React.memo()

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

const StudentCard = memo(({ name }) => {

  console.log('StudentCard rendered');

  return (
    <Text>{name}</Text>
  );

});

export default StudentCard;

Now React can skip rendering the component when its props remain unchanged.

Important: React.memo() is an optimization, not a guarantee that a component will never render again. It is most useful when a component renders frequently and its props usually remain the same.

6. Optimize Calculations Using useMemo()

useMemo() caches the result of a calculation between renders while its dependencies remain unchanged.

For example, imagine that an application has thousands of student records.

Filtering all students repeatedly can become expensive.

Example Without useMemo()

const filteredStudents = students.filter(student =>
  student.name.toLowerCase().includes(search.toLowerCase())
);

This calculation runs whenever the component renders.

Example With useMemo()

import React, { useMemo } from 'react';

const filteredStudents = useMemo(() => {

  return students.filter(student =>
    student.name.toLowerCase().includes(search.toLowerCase())
  );

}, [students, search]);

Now React can reuse the previous filtered result when neither dependency changes.

Remember: Do not use useMemo() for every small calculation. Use it when profiling shows that a calculation is expensive or repeated unnecessarily.

7. Optimize Functions Using useCallback()

useCallback() caches a function reference until its dependencies change.

It is useful when passing callbacks to memoized child components or optimized lists.

Example

import React, { useCallback } from 'react';

const handleStudentPress = useCallback((studentId) => {

  console.log('Selected Student:', studentId);

}, []);

The function reference remains stable across renders until its dependencies change.

This can help prevent unnecessary rendering when combined with React.memo().

8. Why FlatList Is Better for Large Lists

ScrollView renders all of its children at once.

This can create performance problems when displaying hundreds or thousands of records.

FlatList uses virtualization to render a limited window of items around the visible area.

ScrollView Example

import { ScrollView, Text } from 'react-native';

<ScrollView>

  {students.map(student => (
    <Text key={student.id}>
      {student.name}
    </Text>
  ))}

</ScrollView>

FlatList Example

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

<FlatList
  data={students}
  keyExtractor={item => item.id}
  renderItem={({ item }) => (
    <Text>{item.name}</Text>
  )}
/>

For long lists, FlatList is generally a better choice because it virtualizes content.

9. Important FlatList Optimization Properties

React Native provides several properties to help optimize FlatList.

9.1 initialNumToRender

Controls how many items are rendered initially.

initialNumToRender={10}

9.2 windowSize

Controls the number of viewport lengths maintained in the render window.

windowSize={7}

A smaller value may reduce mounted items and memory use, but can increase the chance of blank areas during fast scrolling.

9.3 maxToRenderPerBatch

Controls the maximum number of items rendered in each batch.

maxToRenderPerBatch={8}

9.4 updateCellsBatchingPeriod

Controls the delay between rendering batches.

updateCellsBatchingPeriod={50}

9.5 getItemLayout

If every list item has a fixed height, getItemLayout can avoid measuring item layouts dynamically.

getItemLayout={(data, index) => ({
  length: 64,
  offset: 64 * index,
  index,
})}

These values are examples, not universal best settings. Tune them according to item size, device capability, and scrolling behavior.

10. Create an Optimized Student App

Now we will create a practical application that displays a large student list with search functionality.

The application will include:

  • A list of 1,000 students.

  • Search functionality.

  • Optimized list rendering.

  • Memoized student cards.

  • Efficient filtering.

  • Stable callback functions.

  • Optimized FlatList configuration.

Step 1: Create a New Project

Open your terminal.

Execute:

npx create-expo-app@latest OptimizedStudentApp --template blank

Open the project:

cd OptimizedStudentApp

Start the project:

npx expo start

Step 2: Open App.js

Replace the contents of App.js with the following code.

Complete App.js Code

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

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

// Memoized Student Card

const StudentCard = memo(({ item }) => {

  return (
    <View style={styles.card}>

      <Text style={styles.studentName}>
        {item.name}
      </Text>

      <Text style={styles.studentEmail}>
        {item.email}
      </Text>

    </View>
  );

});

// Main Application

export default function App() {

  const [search, setSearch] = useState('');

  // Generate Student Data

  const students = useMemo(() => {

    return Array.from({ length: 1000 }, (_, index) => ({
      id: String(index + 1),
      name: `Student ${index + 1}`,
      email: `student${index + 1}@example.com`,
    }));

  }, []);

  // Optimize Search Filtering

  const filteredStudents = useMemo(() => {

    const query = search.trim().toLowerCase();

    if (!query) {
      return students;
    }

    return students.filter(student =>
      student.name.toLowerCase().includes(query)
    );

  }, [students, search]);

  // Stable Render Function

  const renderStudent = useCallback(({ item }) => {

    return <StudentCard item={item} />;

  }, []);

  // Stable Key Extractor

  const keyExtractor = useCallback((item) => {
    return item.id;
  }, []);

  return (

    <View style={styles.container}>

      <StatusBar
        backgroundColor="#102A43"
        barStyle="light-content"
      />

      <Text style={styles.heading}>
        Optimized Student App
      </Text>

      <Text style={styles.subtitle}>
        Performance Optimization Demo
      </Text>

      <TextInput
        style={styles.searchInput}
        placeholder="Search students..."
        placeholderTextColor="#777"
        value={search}
        onChangeText={setSearch}
      />

      <Text style={styles.resultText}>
        Total Students: {filteredStudents.length}
      </Text>

      <FlatList
        data={filteredStudents}
        renderItem={renderStudent}
        keyExtractor={keyExtractor}

        initialNumToRender={10}
        maxToRenderPerBatch={8}
        updateCellsBatchingPeriod={50}
        windowSize={7}

        getItemLayout={(data, index) => ({
          length: 64,
          offset: 64 * index,
          index,
        })}

        keyboardShouldPersistTaps="handled"

        showsVerticalScrollIndicator={false}

      />

    </View>

  );

}

// Styles

const styles = StyleSheet.create({

  container: {
    flex: 1,
    backgroundColor: '#F2F5F9',
    paddingTop: 45,
    paddingHorizontal: 16,
  },

  heading: {
    fontSize: 25,
    fontWeight: 'bold',
    color: '#102A43',
  },

  subtitle: {
    fontSize: 14,
    color: '#627D98',
    marginTop: 5,
    marginBottom: 20,
  },

  searchInput: {
    backgroundColor: '#FFFFFF',
    borderRadius: 10,
    padding: 14,
    borderWidth: 1,
    borderColor: '#D9E2EC',
    marginBottom: 15,
  },

  resultText: {
    fontSize: 15,
    fontWeight: '600',
    color: '#243B53',
    marginBottom: 10,
  },

  card: {
    height: 64,
    backgroundColor: '#FFFFFF',
    borderRadius: 10,
    paddingHorizontal: 15,
    justifyContent: 'center',
    marginBottom: 0,
    borderBottomWidth: 1,
    borderBottomColor: '#E6EDF3',
  },

  studentName: {
    fontSize: 16,
    fontWeight: 'bold',
    color: '#102A43',
  },

  studentEmail: {
    fontSize: 12,
    color: '#627D98',
    marginTop: 3,
  },

});

Step 3: Run the Application

Execute:

npx expo start

Scan the QR code using Expo Go or run the application on an appropriate development environment.

Step 4: Test the Application

Perform these actions:

  1. Open the Optimized Student App.

  2. View the student list.

  3. Scroll through the list.

  4. Search for Student 500.

  5. Search for Student 100.

  6. Clear the search field.

  7. Check whether the list updates correctly.

You now have a practical application demonstrating React Native performance optimization techniques.

11. Optimize Images

Large images can increase memory consumption and slow down application loading.

Follow these practices:

  • Use appropriately sized images.

  • Compress images before adding them to the project.

  • Avoid loading unnecessarily large images inside list items.

  • Use thumbnails for list previews.

  • Load full-resolution images only when needed.

  • Avoid unnecessary image resizing during animations.

Example:

import { Image } from 'react-native';

<Image
  source={require('./assets/student.png')}
  style={{
    width: 100,
    height: 100,
  }}
  resizeMode="cover"
/>

Choose image dimensions according to the actual display size.

12. Reduce Application Loading Time

To improve startup performance:

  1. Avoid loading unnecessary data during the first screen render.

  2. Load additional data when users need it.

  3. Avoid expensive calculations during initial rendering.

  4. Use optimized images and assets.

  5. Remove unused dependencies.

  6. Avoid unnecessary JavaScript operations.

  7. Keep the initial screen lightweight.

  8. Use pagination or incremental loading for large datasets.

For API-based applications, load essential information first and fetch secondary content afterward.

13. Avoid Unnecessary Console Logs

Excessive logging can affect JavaScript execution performance, particularly in production applications.

Example:

console.log('Student Data:', students);

Use logs during development when needed, but remove unnecessary logging before release.

For complex applications, use a proper logging strategy instead of printing large objects repeatedly.

14. Understand JavaScript Thread Performance

React Native applications involve JavaScript execution and native UI rendering.

Heavy JavaScript work can delay JavaScript-driven updates and user interactions.

Examples of expensive operations include:

  • Processing very large arrays.

  • Performing complex calculations repeatedly.

  • Parsing large JSON responses.

  • Running expensive synchronous operations.

Solutions include:

  • Optimizing algorithms.

  • Reducing unnecessary calculations.

  • Moving appropriate heavy work outside the critical interaction path.

  • Using efficient list rendering.

  • Profiling before choosing more advanced solutions.

15. Use React Native DevTools to Measure Performance

React Native DevTools helps developers inspect application behavior and identify performance problems.

You can use its tools to:

  • Inspect component rendering.

  • View performance traces.

  • Identify expensive operations.

  • Inspect memory usage.

  • Understand component updates.

Start your Expo project and open React Native DevTools using the available development menu or the Expo CLI shortcut.

Use the Profiler and Performance panels to investigate slow interactions.

Important: Performance profiling should be based on measurements rather than assumptions.

16. Test Performance in Release Builds

Development builds include additional debugging features and may behave differently from production builds.

Always evaluate performance using an appropriate release build.

For Expo projects using EAS Build, you can configure a production build profile and create a build for your target platform.

For example:

eas build --platform android --profile production

Compare startup time, scrolling behavior, memory consumption, and responsiveness on representative devices.

17. Advanced Optimization Tips

For larger applications, consider these additional techniques:

17.1 Pagination

Load data in smaller groups instead of downloading thousands of records at once.

17.2 FlashList

FlashList is an alternative list component designed for efficient list rendering.

Official documentation:

https://shopify.github.io/flash-list/

17.3 Lazy Loading

Load content only when it is required.

17.4 Reduce Component Complexity

Keep frequently rendered list components lightweight.

17.5 Avoid Unnecessary State

Keep temporary state close to the component that needs it.

17.6 Optimize Animations

Use appropriate native-driven or UI-thread animation solutions for smooth animations.

Choose animation libraries compatible with your React Native and Expo versions.

18. Performance Optimization Checklist

Before releasing an application, verify the following:

  • Use FlatList for large lists.

  • Use stable keys for list items.

  • Keep list item components lightweight.

  • Use React.memo() where profiling justifies it.

  • Use useMemo() for expensive calculations.

  • Use useCallback() when stable callback references help.

  • Optimize image dimensions and file sizes.

  • Avoid unnecessary state updates.

  • Remove excessive console logs.

  • Reduce unnecessary startup work.

  • Test on real Android and iOS devices.

  • Measure performance in release builds.

19. Final Project Outcome

After completing this tutorial, you will understand how to:

  • Build a performance-optimized React Native application.

  • Display large datasets efficiently.

  • Use FlatList configuration options.

  • Reduce unnecessary component rendering.

  • Optimize calculations and callback functions.

  • Improve image loading.

  • Identify performance bottlenecks.

  • Measure application performance.

20. Conclusion

Performance optimization is an important skill for professional React Native developers.

Using the correct list components, reducing unnecessary renders, optimizing images, and measuring application performance can significantly improve the user experience.

Remember that optimization should always be based on real performance measurements.

Practice Task: Extend the Optimized Student App by adding student profile images, pagination, and a student details screen. Then measure the performance before and after your improvements.

21. Frequently Asked Questions

Q1. Why is my React Native application slow?

Common reasons include unnecessary re-renders, large images, expensive JavaScript operations, and inefficient list rendering.

Q2. What is React.memo()?

React.memo() can prevent unnecessary component rendering when props remain unchanged.

Q3. What is the difference between useMemo() and useCallback()?

useMemo() caches a calculated value, while useCallback() caches a function reference.

Q4. Why is FlatList better than ScrollView for large lists?

FlatList uses virtualization to render a limited window of items instead of rendering every item at once.

Q5. How can I improve React Native app loading speed?

Reduce initial workload, optimize images, remove unnecessary dependencies, and load secondary content when needed.

Q6. How can I measure React Native performance?

Use React Native DevTools, performance traces, profiling tools, and release-build testing on real devices.

←
Post 34 of 39
→