Introduction
React Native remains one of the most popular frameworks for building cross-platform mobile apps. In 2026, with over 40% of mobile developers using it according to the Stack Overflow Developer Survey 2025, the demand for efficient, reusable code is higher than ever. But even experienced developers hit roadblocks: crafting a complex UI component, setting up deep navigation, or handling API errors gracefully. That is where prompts—precise, reusable code generation instructions—come in. This article offers 10 carefully crafted prompts for React Native, covering UI components, navigation, and API integration. Each prompt includes a real-world example and output to boost your productivity. Whether you are a beginner or a seasoned engineer, these prompts will save you hours of boilerplate code.
1. Reusable Button Component with Loading State
Prompt: "Generate a reusable React Native button component that supports a loading spinner, disabled state, and custom colors. Use TypeScript and StyleSheet."
Why you need it: A consistent button component is the backbone of any app. This prompt produces a component that handles three common states: idle, loading, and disabled.
Example output:
import React from 'react';
import { TouchableOpacity, Text, ActivityIndicator, StyleSheet, ViewStyle } from 'react-native';
interface ButtonProps {
title: string;
onPress: () => void;
loading?: boolean;
disabled?: boolean;
backgroundColor?: string;
textColor?: string;
}
const CustomButton: React.FC<ButtonProps> = ({
title,
onPress,
loading = false,
disabled = false,
backgroundColor = '#3498db',
textColor = '#fff',
}) => {
return (
<TouchableOpacity
style={[
styles.button,
{ backgroundColor: disabled ? '#bdc3c7' : backgroundColor },
disabled && styles.disabled,
]}
onPress={onPress}
disabled={disabled || loading}
activeOpacity={0.7}
>
{loading ? (
<ActivityIndicator size="small" color={textColor} />
) : (
<Text style={[styles.text, { color: textColor }]}>{title}</Text>
)}
</TouchableOpacity>
);
};
const styles = StyleSheet.create({
button: {
paddingVertical: 12,
paddingHorizontal: 24,
borderRadius: 8,
alignItems: 'center',
justifyContent: 'center',
minWidth: 120,
},
disabled: {
opacity: 0.6,
},
text: {
fontSize: 16,
fontWeight: '600',
},
});
export default CustomButton;
Usage: <CustomButton title="Submit" onPress={handleSubmit} loading={isSubmitting} />
2. Animated FlatList with Pull-to-Refresh and Load More
Prompt: "Create a React Native FlatList with pull-to-refresh, infinite scroll load-more functionality, and a custom refresh animation. Use functional components and hooks."
Why you need it: Lists are everywhere in mobile apps—feeds, chats, product lists. This prompt produces a performant list with standard UX patterns.
Example output:
import React, { useState, useCallback } from 'react';
import { FlatList, RefreshControl, ActivityIndicator, View, Text, StyleSheet } from 'react-native';
interface Item {
id: string;
title: string;
}
const AnimatedList: React.FC = () => {
const [data, setData] = useState<Item[]>([]);
const [refreshing, setRefreshing] = useState(false);
const [loadingMore, setLoadingMore] = useState(false);
const [page, setPage] = useState(1);
const fetchData = async (pageNum: number) => {
// Simulated API call
const response = await fetch(`https://api.example.com/items?page=${pageNum}&limit=20`);
const newItems: Item[] = await response.json();
return newItems;
};
const handleRefresh = useCallback(async () => {
setRefreshing(true);
const freshData = await fetchData(1);
setData(freshData);
setPage(1);
setRefreshing(false);
}, []);
const handleLoadMore = useCallback(async () => {
if (loadingMore) return;
setLoadingMore(true);
const nextPage = page + 1;
const moreData = await fetchData(nextPage);
setData(prev => [...prev, ...moreData]);
setPage(nextPage);
setLoadingMore(false);
}, [page, loadingMore]);
const renderItem = ({ item }: { item: Item }) => (
<View style={styles.item}>
<Text>{item.title}</Text>
</View>
);
return (
<FlatList
data={data}
renderItem={renderItem}
keyExtractor={item => item.id}
refreshControl={
<RefreshControl refreshing={refreshing} onRefresh={handleRefresh} />
}
onEndReached={handleLoadMore}
onEndReachedThreshold={0.5}
ListFooterComponent={loadingMore ? <ActivityIndicator size="small" /> : null}
/>
);
};
const styles = StyleSheet.create({
item: {
padding: 16,
borderBottomWidth: 1,
borderBottomColor: '#eee',
},
});
export default AnimatedList;
3. Bottom Tab Navigator with Custom Icons
Prompt: "Set up a React Navigation bottom tab navigator with five tabs (Home, Search, Add, Notifications, Profile). Use custom icon components and badge counts from a context."
Why you need it: Navigation is the skeleton of any app. This prompt integrates react-navigation with a real-world badge pattern.
Example output:
import React from 'react';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { View, Text, StyleSheet } from 'react-native';
import HomeScreen from '../screens/HomeScreen';
import SearchScreen from '../screens/SearchScreen';
import AddScreen from '../screens/AddScreen';
import NotificationsScreen from '../screens/NotificationsScreen';
import ProfileScreen from '../screens/ProfileScreen';
import { useNotifications } from '../context/NotificationContext';
const Tab = createBottomTabNavigator();
const CustomTabIcon = ({ name, focused }: { name: string; focused: boolean }) => (
<View style={[styles.iconContainer, focused && styles.activeIcon]}>
<Text>{name === 'Home' ? '🏠' : name === 'Search' ? '🔍' : name === 'Add' ? '➕' : name === 'Notifications' ? '🔔' : '👤'}</Text>
</View>
);
const BottomTabs: React.FC = () => {
const { unreadCount } = useNotifications();
return (
<Tab.Navigator
screenOptions={({ route }) => ({
tabBarIcon: ({ focused }) => <CustomTabIcon name={route.name} focused={focused} />,
tabBarActiveTintColor: '#3498db',
tabBarInactiveTintColor: '#999',
headerShown: false,
})}
>
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="Search" component={SearchScreen} />
<Tab.Screen name="Add" component={AddScreen} />
<Tab.Screen
name="Notifications"
component={NotificationsScreen}
options={{
tabBarBadge: unreadCount > 0 ? unreadCount : undefined,
}}
/>
<Tab.Screen name="Profile" component={ProfileScreen} />
</Tab.Navigator>
);
};
const styles = StyleSheet.create({
iconContainer: { padding: 4 },
activeIcon: { opacity: 1 },
});
export default BottomTabs;
4. Stack Navigator with Type-Safe Params
Prompt: "Create a stack navigator with TypeScript type checking for route params. Include a main list screen and a detail screen that receives an ID and title."
Why you need it: Type safety prevents runtime crashes when passing data between screens.
Example output:
import React from 'react';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import ListScreen from '../screens/ListScreen';
import DetailScreen from '../screens/DetailScreen';
export type RootStackParamList = {
List: undefined;
Detail: { id: string; title: string };
};
const Stack = createNativeStackNavigator<RootStackParamList>();
const AppNavigator: React.FC = () => {
return (
<Stack.Navigator initialRouteName="List">
<Stack.Screen name="List" component={ListScreen} options={{ title: 'Items' }} />
<Stack.Screen name="Detail" component={DetailScreen} options={({ route }) => ({ title: route.params.title })} />
</Stack.Navigator>
);
};
export default AppNavigator;
Usage in DetailScreen: const { id, title } = route.params;
5. API Service with Axios and Error Handling
Prompt: "Build an API service module using Axios with interceptors for token refresh, request/response logging, and centralized error handling. Include TypeScript types."
Why you need it: A robust API layer reduces code duplication and handles auth errors gracefully.
Example output:
import axios, { AxiosInstance, AxiosError, InternalAxiosRequestConfig } from 'axios';
import AsyncStorage from '@react-native-async-storage/async-storage';
const api: AxiosInstance = axios.create({
baseURL: 'https://api.example.com',
timeout: 10000,
headers: { 'Content-Type': 'application/json' },
});
// Request interceptor to attach token
api.interceptors.request.use(
async (config: InternalAxiosRequestConfig) => {
const token = await AsyncStorage.getItem('authToken');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
console.log(`[API] ${config.method?.toUpperCase()} ${config.url}`);
return config;
},
(error) => Promise.reject(error)
);
// Response interceptor for token refresh
api.interceptors.response.use(
(response) => response,
async (error: AxiosError) => {
if (error.response?.status === 401) {
// Attempt token refresh
const refreshToken = await AsyncStorage.getItem('refreshToken');
if (refreshToken) {
try {
const { data } = await axios.post('https://api.example.com/auth/refresh', { refreshToken });
await AsyncStorage.setItem('authToken', data.accessToken);
// Retry original request
if (error.config) {
error.config.headers.Authorization = `Bearer ${data.accessToken}`;
return api(error.config);
}
} catch {
// Redirect to login
}
}
}
return Promise.reject(error);
}
);
export default api;
6. Custom Hook for Fetching Data with Loading and Error States
Prompt: "Create a custom React hook useFetch that takes a URL and returns data, loading, error, and a refetch function. Use AbortController for cleanup."
Why you need it: Data fetching hooks are the modern way to manage async state without a library.
Example output:
import { useState, useEffect, useCallback, useRef } from 'react';
interface UseFetchResult<T> {
data: T | null;
loading: boolean;
error: string | null;
refetch: () => void;
}
function useFetch<T>(url: string): UseFetchResult<T> {
const [data, setData] = useState<T | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const abortControllerRef = useRef<AbortController | null>(null);
const fetchData = useCallback(async () => {
abortControllerRef.current?.abort();
const controller = new AbortController();
abortControllerRef.current = controller;
setLoading(true);
setError(null);
try {
const response = await fetch(url, { signal: controller.signal });
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
const json = await response.json();
setData(json);
} catch (err: any) {
if (err.name !== 'AbortError') {
setError(err.message || 'Something went wrong');
}
} finally {
setLoading(false);
}
}, [url]);
useEffect(() => {
fetchData();
return () => abortControllerRef.current?.abort();
}, [fetchData]);
return { data, loading, error, refetch: fetchData };
}
export default useFetch;
7. Form with Validation Using React Hook Form
Prompt: "Implement a registration form with email, password, and confirm password fields using react-hook-form and yup validation. Show inline error messages."
Why you need it: Forms are a pain point. This prompt delivers a tested pattern with minimal boilerplate.
Example output:
import React from 'react';
import { View, Text, TextInput, TouchableOpacity, StyleSheet } from 'react-native';
import { useForm, Controller } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
import * as yup from 'yup';
const schema = yup.object({
email: yup.string().email('Invalid email').required('Email is required'),
password: yup.string().min(6, 'Min 6 characters').required('Password is required'),
confirmPassword: yup.string().oneOf([yup.ref('password')], 'Passwords must match').required('Confirm password'),
});
type FormData = yup.InferType<typeof schema>;
const RegistrationForm: React.FC = () => {
const { control, handleSubmit, formState: { errors } } = useForm<FormData>({
resolver: yupResolver(schema),
});
const onSubmit = (data: FormData) => {
console.log('Form data:', data);
};
return (
<View style={styles.container}>
<Controller
control={control}
name="email"
render={({ field: { onChange, onBlur, value } }) => (
<View>
<TextInput style={styles.input} placeholder="Email" onBlur={onBlur} onChangeText={onChange} value={value} />
{errors.email && <Text style={styles.error}>{errors.email.message}</Text>}
</View>
)}
/>
<Controller
control={control}
name="password"
render={({ field: { onChange, onBlur, value } }) => (
<View>
<TextInput style={styles.input} placeholder="Password" secureTextEntry onBlur={onBlur} onChangeText={onChange} value={value} />
{errors.password && <Text style={styles.error}>{errors.password.message}</Text>}
</View>
)}
/>
<Controller
control={control}
name="confirmPassword"
render={({ field: { onChange, onBlur, value } }) => (
<View>
<TextInput style={styles.input} placeholder="Confirm Password" secureTextEntry onBlur={onBlur} onChangeText={onChange} value={value} />
{errors.confirmPassword && <Text style={styles.error}>{errors.confirmPassword.message}</Text>}
</View>
)}
/>
<TouchableOpacity style={styles.button} onPress={handleSubmit(onSubmit)}>
<Text style={styles.buttonText}>Register</Text>
</TouchableOpacity>
</View>
);
};
const styles = StyleSheet.create({
container: { padding: 20 },
input: { borderWidth: 1, borderColor: '#ccc', borderRadius: 8, padding: 12, marginBottom: 8 },
error: { color: 'red', marginBottom: 8 },
button: { backgroundColor: '#3498db', padding: 14, borderRadius: 8, alignItems: 'center' },
buttonText: { color: '#fff', fontWeight: '600' },
});
export default RegistrationForm;
8. Modal Component with Overlay and Animation
Prompt: "Build a reusable modal component that slides up from the bottom, has a semi-transparent overlay, and supports a close button. Use Animated API."
Why you need it: Modals are essential for dialogs, filters, and menus.
Example output:
import React, { useRef, useEffect } from 'react';
import { Modal as RNModal, Animated, TouchableOpacity, View, StyleSheet, Dimensions } from 'react-native';
const { height: SCREEN_HEIGHT } = Dimensions.get('window');
interface SlideUpModalProps {
visible: boolean;
onClose: () => void;
children: React.ReactNode;
}
const SlideUpModal: React.FC<SlideUpModalProps> = ({ visible, onClose, children }) => {
const translateY = useRef(new Animated.Value(SCREEN_HEIGHT)).current;
useEffect(() => {
if (visible) {
Animated.timing(translateY, {
toValue: 0,
duration: 300,
useNativeDriver: true,
}).start();
} else {
Animated.timing(translateY, {
toValue: SCREEN_HEIGHT,
duration: 300,
useNativeDriver: true,
}).start();
}
}, [visible, translateY]);
return (
<RNModal transparent visible={visible} animationType="none" onRequestClose={onClose}>
<TouchableOpacity style={styles.overlay} activeOpacity={1} onPress={onClose}>
<Animated.View style={[styles.modalContent, { transform: [{ translateY }] }]}>
<TouchableOpacity activeOpacity={1}>
<View style={styles.closeButton}>
<TouchableOpacity onPress={onClose}>
<Text>Close</Text>
</TouchableOpacity>
</View>
{children}
</TouchableOpacity>
</Animated.View>
</TouchableOpacity>
</RNModal>
);
};
const styles = StyleSheet.create({
overlay: { flex: 1, backgroundColor: 'rgba(0,0,0,0.5)', justifyContent: 'flex-end' },
modalContent: { backgroundColor: '#fff', borderTopLeftRadius: 16, borderTopRightRadius: 16, padding: 20, minHeight: 300 },
closeButton: { alignItems: 'flex-end', marginBottom: 12 },
});
export default SlideUpModal;
9. Context Provider for Theme (Light/Dark Mode)
Prompt: "Create a ThemeContext that provides light and dark color schemes, with a toggle function. Use AsyncStorage to persist the preference."
Why you need it: Dark mode is a must-have feature. This prompt gives you a scalable solution.
Example output:
import React, { createContext, useContext, useState, useEffect, useMemo } from 'react';
import AsyncStorage from '@react-native-async-storage/async-storage';
type Theme = 'light' | 'dark';
interface ThemeColors {
background: string;
text: string;
primary: string;
}
const lightColors: ThemeColors = { background: '#fff', text: '#333', primary: '#3498db' };
const darkColors: ThemeColors = { background: '#1a1a2e', text: '#eee', primary: '#bb86fc' };
interface ThemeContextValue {
theme: Theme;
colors: ThemeColors;
toggleTheme: () => void;
}
const ThemeContext = createContext<ThemeContextValue>({
theme: 'light',
colors: lightColors,
toggleTheme: () => {},
});
export const ThemeProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const [theme, setTheme] = useState<Theme>('light');
useEffect(() => {
AsyncStorage.getItem('theme').then((stored) => {
if (stored === 'dark' || stored === 'light') setTheme(stored);
});
}, []);
const toggleTheme = async () => {
const newTheme = theme === 'light' ? 'dark' : 'light';
setTheme(newTheme);
await AsyncStorage.setItem('theme', newTheme);
};
const value = useMemo(() => ({
theme,
colors: theme === 'light' ? lightColors : darkColors,
toggleTheme,
}), [theme]);
return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>;
};
export const useTheme = () => useContext(ThemeContext);
10. Performance-Optimized Image Gallery with Lazy Loading
Prompt: "Create a grid image gallery component that uses FastImage for caching, lazy loads images on scroll, and displays a placeholder. Use FlashList for performance."
Why you need it: Image-heavy apps drain memory. This prompt uses proven libraries for speed.
Example output:
import React, { useState } from 'react';
import { View, Dimensions, StyleSheet } from 'react-native';
import FastImage from 'react-native-fast-image';
import { FlashList } from '@shopify/flash-list';
const { width } = Dimensions.get('window');
const ITEM_WIDTH = width / 3;
interface GalleryItem {
id: string;
url: string;
}
const ImageGallery: React.FC<{ images: GalleryItem[] }> = ({ images }) => {
const [visibleRange, setVisibleRange] = useState({ start: 0, end: 30 });
const renderItem = ({ item }: { item: GalleryItem }) => (
<View style={styles.imageContainer}>
<FastImage
style={styles.image}
source={{ uri: item.url, priority: FastImage.priority.normal }}
resizeMode={FastImage.resizeMode.cover}
/>
</View>
);
return (
<FlashList
data={images.slice(visibleRange.start, visibleRange.end)}
renderItem={renderItem}
keyExtractor={item => item.id}
numColumns={3}
estimatedItemSize={ITEM_WIDTH}
onEndReached={() => setVisibleRange(prev => ({ ...prev, end: prev.end + 30 }))}
onEndReachedThreshold={0.5}
/>
);
};
const styles = StyleSheet.create({
imageContainer: { width: ITEM_WIDTH, height: ITEM_WIDTH, padding: 2 },
image: { flex: 1, borderRadius: 4 },
});
export default ImageGallery;
Conclusion
These 10 prompts cover the most common pain points in React Native development: reusable components, navigation, API integration, state management, and performance. By using them as a starting point, you can cut your development time significantly and produce cleaner, more maintainable code. Remember to adjust the prompts to your specific project needs—change the API endpoints, tweak the colors, or add more validation rules. Start implementing these patterns in your next project, and you will see immediate improvements in both speed and code quality. If you have a favorite prompt that we missed, share it in the comments below. Happy coding!
Comments