React Native FlatList Optimization: Smooth Scrolling with Expo & TypeScript

React Native lists can become sluggish as data grows — this short guide shows practical, TypeScript-friendly techniques to get smooth scrolling back. Using Expo (SDK 51) and React Native 0.75.4 as a baseline, you'll learn how to tune FlatList props, memoize item rendering, fetch data efficiently with Axios, and deploy fixes via EAS OTA updates. Follow the step-by-step examples and code snippets to improve rendering performance on both iOS and Android.
Tooling Compatibility Matrix
| Tool | Version | Notes |
|---|---|---|
| React Native | 0.75.4 | Compatible with Expo SDK 51 |
| Expo | ~51.0.0 | Supports EAS Build/Update, New Architecture |
| Node.js | 20.x (LTS) | Required for EAS CLI |
| Xcode | 16+ | iOS 18 SDK, macOS only |
| Android Studio | 2024.3.2+ | Android SDK 35, NDK r25+ |
| Axios | 1.11.0 | For 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 BundleStep 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
initialNumToRenderandwindowSizein FlatList configuration. Ensurememois 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 listand update per Expo’s compatibility guide. - Build Fails: Ensure Xcode 16+ and Android SDK 35. Clear caches (
rm -rf ~/Library/Developer/Xcode/DerivedDataor./gradlew clean). - OTA Issues: Verify
EXPO_TOKENandapp.config.js’supdates.url. Runeas update:configure.
End-of-Article Recap
You’ve learned to:
- Optimize FlatList configuration with props like
initialNumToRenderandremoveClippedSubviews. - Improve render optimization with
memofor 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.jswith 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 TouchConclusion
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
- dopebase.com - Mobile app development resources and templates
- instamobile.io - Production-ready React Native templates
- instaflutter.com - Flutter templates for cross-platform development
- reactnative.dev - Official React Native documentation
- docs.expo.dev - Expo SDK and EAS documentation
- axios-http.com - Axios documentation
- Expo Snack - Quick prototyping tool