Skip to main content

React Native FlatList Optimization (2025): Smooth Scrolling with Expo & TypeScript

· 10 min read
Mobile Developer
Last updated on August 17, 2025

React native flatlist optimization

Note: This guide uses React Native 0.75.4 and Expo SDK 51, which remain supported as of August 2025 for React Native 0.75 compatibility. Expo SDK 53 offers improved features, including enhanced New Architecture support. For new projects, consider using SDK 53 and enabling the New Architecture early to avoid deprecations of the old architecture expected by late 2025 or early 2026. Check reactnative.dev and docs.expo.dev for updates if using newer versions like React Native 0.76 or Expo SDK 53.

Tooling Compatibility Matrix

ToolVersionNotes
React Native0.75.4Compatible with Expo SDK 51
Expo~51.0.0Supports EAS Build/Update, New Architecture
Node.js20.x (LTS)Required for EAS CLI
Xcode16+iOS 18 SDK, macOS only
Android Studio2024.3.2+Android SDK 35, NDK r25+
Axios1.11.0For data fetching in examples

Optimizing FlatList in React Native ensures smooth scrolling for large datasets, enhancing user experience on iOS (Xcode 16+, iOS 18 SDK) and Android (SDK 35). This guide shows you how to optimize FlatList in React Native 0.75.4 with Expo SDK 51, leveraging FlatList configuration, render optimization, and data fetching with Axios. Expo SDK 51 supports the New Architecture, including bridgeless mode, for improved performance. We’ll use TypeScript for type safety and Over-The-Air (OTA) updates via EAS for deployment. For upgrading to newer React Native versions, see the React Native upgrade guide. For pre-optimized templates, explore Instamobile or Dopebase.

Let’s create a smooth-scrolling FlatList!


Prerequisites

Ensure you have:

  • Node.js (v20.x or later, LTS): nodejs.org
  • npm (v10.x or later)
  • EAS CLI: For Expo management
  • Android Studio (2024.3.2+): Android SDK 35, NDK r25+
  • Xcode (16+): iOS 18 SDK (macOS only)
  • VS Code or similar editor
  • Git: For version control
  • A React Native project (Expo SDK 51)
  • A REST API endpoint (e.g., jsonplaceholder.typicode.com)
  • macOS: For iOS builds

Note: Expo Go supports SDK 51 but may have limitations with custom native code or New Architecture features. Use Expo Snack for quick prototyping.

What is FlatList? FlatList is a React Native component that efficiently renders large lists using virtualization, displaying only visible items to save memory and boost performance. Learn more in React Native’s FlatList guide.


Step 1: Set Up Your Project

Create a React Native project with TypeScript and configure it for FlatList optimization.

1.1 Create Project

Install EAS CLI:

npm install -g eas-cli

Create a project:

npx create-expo-app MyApp --template blank-typescript

Navigate to the project:

cd MyApp

1.2 Configure app.config.js

Update app.config.js for OTA updates and flexibility:

export default () => ({
name: "MyApp",
slug: "myapp",
version: "1.0.0",
orientation: "portrait",
icon: ".https://docs.instamobile.io/assets/icon.png",
splash: {
image: ".https://docs.instamobile.io/assets/splash.png",
resizeMode: "contain",
backgroundColor: "#ffffff"
},
ios: {
bundleIdentifier: "com.myapp.app",
buildNumber: "1.0.0"
},
android: {
package: "com.myapp.app",
versionCode: 1
},
updates: {
enabled: true,
url: "https://u.expo.dev/your-project-id",
checkAutomatically: "ON_LOAD",
fallbackToCacheTimeout: 0
}
});

1.3 Test Locally

Run the app:

npx expo start

Press i for iOS or a for Android to verify setup. Use Expo Snack for quick testing without local setup.


Mega Bundle Sale is ON! Get ALL of our React Native codebases at 90% OFF discount 🔥

Get the Mega Bundle

Step 2: Configure FlatList Props

Optimize FlatList configuration for smooth scrolling.

2.1 Install Dependencies

Add Axios for data fetching:

npm install [email protected]

2.2 Set Up Basic FlatList

Update src/App.tsx with an optimized FlatList:

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

type Item = { id: number; title: string };

export default function App() {
const [items, setItems] = useState<Item[]>([]);
const [loading, setLoading] = useState(true);

useEffect(() => {
const fetchItems = async () => {
try {
const response = await axios.get('https://jsonplaceholder.typicode.com/posts');
setItems(response.data);
} catch (error) {
console.error('Error fetching items:', error);
} finally {
setLoading(false);
}
};
fetchItems();
}, []);

return (
<View style={{ flex: 1, padding: 20 }}>
{loading ? (
<Text>Loading...</Text>
) : (
<FlatList
data={items}
keyExtractor={item => item.id.toString()}
renderItem={({ item }) => <Text>{item.title}</Text>}
initialNumToRender={10}
maxToRenderPerBatch={10}
windowSize={5}
removeClippedSubviews={true}
/>
)}
</View>
);
}

What is FlatList configuration? Optimizing props like initialNumToRender, maxToRenderPerBatch, and removeClippedSubviews reduces rendering overhead, ensuring smoother scrolling.


Step 3: Optimize Render Items

Improve render optimization for FlatList items.

3.1 Create Reusable Item Component

Create src/components/Item.tsx for optimized rendering:

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

type Props = { title: string };

const Item = ({ title }: Props) => (
<View style={styles.container}>
<Text>{title}</Text>
</View>
);

const styles = StyleSheet.create({
container: { padding: 10, borderBottomWidth: 1, borderBottomColor: '#ccc' },
});

export default memo(Item);

3.2 Update FlatList

Update src/App.tsx to use the memoized component:

import { useEffect, useState } from 'react';
import { View, Text, FlatList } from 'react-native';
import axios from 'axios';
import Item from './components/Item';

type ItemType = { id: number; title: string };

export default function App() {
const [items, setItems] = useState<ItemType[]>([]);
const [loading, setLoading] = useState(true);

useEffect(() => {
const fetchItems = async () => {
try {
const response = await axios.get('https://jsonplaceholder.typicode.com/posts');
setItems(response.data);
} catch (error) {
console.error('Error fetching items:', error);
} finally {
setLoading(false);
}
};
fetchItems();
}, []);

return (
<View style={{ flex: 1, padding: 20 }}>
{loading ? (
<Text>Loading...</Text>
) : (
<FlatList
data={items}
keyExtractor={item => item.id.toString()}
renderItem={({ item }) => <Item title={item.title} />}
initialNumToRender={10}
maxToRenderPerBatch={10}
windowSize={5}
removeClippedSubviews={true}
/>
)}
</View>
);
}

What is render optimization? Using memo prevents unnecessary re-renders of list items when props are unchanged, improving performance.


Step 4: Fetch Data Efficiently

Optimize data fetching to reduce load times.

4.1 Create API Service

Create src/api/api.ts:

import axios, { AxiosInstance } from 'axios';

const api: AxiosInstance = axios.create({
baseURL: 'https://jsonplaceholder.typicode.com',
timeout: 10000,
});

export const getItems = async () => {
try {
const response = await api.get('/posts');
return response.data;
} catch (error) {
throw new Error('Failed to fetch items');
}
};

4.2 Update App with API Service

Update src/App.tsx:

import { useEffect, useState } from 'react';
import { View, Text, FlatList } from 'react-native';
import { getItems } from './api/api';
import Item from './components/Item';

type ItemType = { id: number; title: string };

export default function App() {
const [items, setItems] = useState<ItemType[]>([]);
const [loading, setLoading] = useState(true);

useEffect(() => {
const fetchItems = async () => {
try {
const data = await getItems();
setItems(data);
} catch (error) {
console.error('Error fetching items:', error);
} finally {
setLoading(false);
}
};
fetchItems();
}, []);

return (
<View style={{ flex: 1, padding: 20 }}>
{loading ? (
<Text>Loading...</Text>
) : (
<FlatList
data={items}
keyExtractor={item => item.id.toString()}
renderItem={({ item }) => <Item title={item.title} />}
initialNumToRender={10}
maxToRenderPerBatch={10}
windowSize={5}
removeClippedSubviews={true}
/>
)}
</View>
);
}

Step 5: Test FlatList Performance

Verify smooth scrolling with automated and manual tests.

5.1 Set Up Automated Tests

Install testing dependencies:

npm install --save-dev jest @types/jest ts-jest @testing-library/react-native @testing-library/jest-native

Configure package.json:

{
"scripts": {
"test": "jest"
},
"jest": {
"preset": "react-native",
"transform": {
"^.+\\.tsx?$": "ts-jest"
},
"setupFilesAfterEnv": ["@testing-library/jest-native/extend-expect"]
}
}

Add test in src/__tests__/App.test.tsx:

import { render, waitFor } from '@testing-library/react-native';
import App from '../App';
import { getItems } from '../api/api';
import axios from 'axios';

jest.mock('axios');

test('renders FlatList items', async () => {
const mockItems = [{ id: 1, title: 'Test Item' }];
axios.get.mockResolvedValue({ data: mockItems });
const { getByText } = render(<App />);
await waitFor(() => expect(getByText('Test Item')).toBeTruthy());
});

Run tests:

npm test

5.2 Manual Testing

Test scrolling on iOS/Android devices with large lists to ensure smoothness.


Step 6: Deploy with OTA Updates

Deploy optimizations with Over-The-Air (OTA) updates via EAS.

6.1 Configure OTA

Install expo-updates:

npm install expo-updates

Check compatibility at docs.expo.dev.

Ensure app.config.js includes OTA settings (Step 1.2).

Update src/App.tsx for OTA updates:

import { useEffect, useState } from 'react';
import { View, Text, FlatList } from 'react-native';
import * as Updates from 'expo-updates';
import { getItems } from './api/api';
import Item from './components/Item';

type ItemType = { id: number; title: string };

export default function App() {
const [items, setItems] = useState<ItemType[]>([]);
const [loading, setLoading] = useState(true);

useEffect(() => {
const checkForUpdates = async () => {
try {
const update = await Updates.checkForUpdateAsync();
if (update.isAvailable) {
await Updates.fetchUpdateAsync();
await Updates.reloadAsync();
}
} catch (e) {
console.error('Update check failed:', e);
}
};
if (!__DEV__) {
checkForUpdates();
}

const fetchItems = async () => {
try {
const data = await getItems();
setItems(data);
} catch (error) {
console.error('Error fetching items:', error);
} finally {
setLoading(false);
}
};
fetchItems();
}, []);

return (
<View style={{ flex: 1, padding: 20 }}>
{loading ? (
<Text>Loading...</Text>
) : (
<FlatList
data={items}
keyExtractor={item => item.id.toString()}
renderItem={({ item }) => <Item title={item.title} />}
initialNumToRender={10}
maxToRenderPerBatch={10}
windowSize={5}
removeClippedSubviews={true}
/>
)}
</View>
);
}

6.2 Publish Update

Configure EAS Update:

eas update:configure

Publish:

eas update --branch production

Test with a preview build:

eas build --profile preview --platform all

Troubleshooting Tips

  • Slow Scrolling: Adjust initialNumToRender and windowSize in FlatList configuration. Ensure memo is used in render optimization.
  • Data Fetching Issues: Verify API endpoint and network. Log errors with console.error.
  • Android Build Hangs: For React Native 0.75.4, check Gradle memory settings or clear caches (./gradlew clean). See Stack Overflow for solutions.
  • Dependency Issues: Run npm list and update per Expo’s compatibility guide.
  • Build Fails: Ensure Xcode 16+ and Android SDK 35. Clear caches (rm -rf ~/Library/Developer/Xcode/DerivedData or ./gradlew clean).
  • OTA Issues: Verify EXPO_TOKEN and app.config.js’s updates.url. Run eas update:configure.

End-of-Article Recap

You’ve learned to:

  • Optimize FlatList configuration with props like initialNumToRender and removeClippedSubviews.
  • Improve render optimization with memo for efficient rendering.
  • Perform data fetching with Axios and deploy updates via OTA, leveraging Expo SDK 51’s New Architecture support.

Quick Reference Checklist

  • Create project with npx create-expo-app MyApp --template blank-typescript.
  • Configure app.config.js with OTA settings.
  • Install Axios (npm install [email protected]) for data fetching.
  • Set up FlatList configuration with optimized props.
  • Create memoized item component for render optimization.
  • Implement API service in src/api/api.ts.
  • Test with Jest and manual scrolling on devices.
  • Deploy updates with EAS Update (eas update --branch production).

Looking for a custom mobile application?

Our team of expert mobile developers can help you build a custom mobile app that meets your specific needs.

Get in Touch

Conclusion

You’ve optimized FlatList in your React Native 0.75.4 app with Expo SDK 51, ensuring smooth scrolling with Axios 1.11.0 and OTA updates via EAS. For new projects, adopt Expo SDK 53 with the New Architecture to future-proof against old architecture deprecations. For upgrading to newer React Native versions, see the React Native upgrade guide. For pre-optimized templates, explore Instamobile or Dopebase. Dive deeper with Expo’s documentation or join the community at reactnative.dev.

Additional Resources