Build advanced UI components and implement smooth animations in React Native.
Build advanced UI components and implement smooth animations in React Native.
Master FlatList and SectionList components for efficient rendering of large lists in React Native applications
Content by: Ronak Macwan
React Native Developer
FlatList is a performant interface for rendering basic, flat lists with support for most common use cases like horizontal mode, header/footer, pull-to-refresh, and more.
SectionList is a performant interface for rendering sectioned lists with support for sticky section headers, section separators, and more.
Use getItemLayout, keyExtractor, and removeClippedSubviews for optimal performance with large datasets.
import React, { useState, useEffect } from 'react';
import { View, Text, FlatList, TouchableOpacity, StyleSheet, RefreshControl, ActivityIndicator } from 'react-native';
const AdvancedFlatList = () => {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(false);
const [refreshing, setRefreshing] = useState(false);
const [page, setPage] = useState(1);
const loadData = async (pageNum = 1, isRefresh = false) => {
if (isRefresh) {
setRefreshing(true);
} else {
setLoading(true);
}
try {
// Simulate API call
await new Promise(resolve => setTimeout(resolve, 1000));
const newData = Array.from({ length: 20 }, (_, i) => ({
id: (pageNum - 1) * 20 + i + 1,
title: `Item ${(pageNum - 1) * 20 + i + 1}`,
subtitle: `This is subtitle for item ${(pageNum - 1) * 20 + i + 1}`,
timestamp: new Date().toISOString(),
}));
if (isRefresh) {
setData(newData);
setPage(1);
} else {
setData(prev => [...prev, ...newData]);
setPage(pageNum);
}
} catch (error) {
console.error('Error loading data:', error);
} finally {
setLoading(false);
setRefreshing(false);
}
};
useEffect(() => {
loadData();
}, []);
const onRefresh = () => {
loadData(1, true);
};
const onEndReached = () => {
if (!loading) {
loadData(page + 1);
}
};
const renderItem = ({ item, index }) => (
<TouchableOpacity style={styles.item}>
<Text style={styles.itemTitle}>{item.title}</Text>
<Text style={styles.itemSubtitle}>{item.subtitle}</Text>
<Text style={styles.itemTimestamp}>
{new Date(item.timestamp).toLocaleString()}
</Text>
</TouchableOpacity>
);
const renderHeader = () => (
<View style={styles.header}>
<Text style={styles.headerTitle}>Advanced FlatList</Text>
<Text style={styles.headerSubtitle}>Pull to refresh, infinite scroll</Text>
</View>
);
const renderFooter = () => {
if (!loading) return null;
return (
<View style={styles.footer}>
<ActivityIndicator size="small" color="#2196F3" />
<Text style={styles.footerText}>Loading more...</Text>
</View>
);
};
const renderEmpty = () => (
<View style={styles.empty}>
<Text style={styles.emptyText}>No data available</Text>
</View>
);
return (
<FlatList
data={data}
renderItem={renderItem}
keyExtractor={item => item.id.toString()}
ListHeaderComponent={renderHeader}
ListFooterComponent={renderFooter}
ListEmptyComponent={renderEmpty}
refreshControl={
<RefreshControl
refreshing={refreshing}
onRefresh={onRefresh}
colors={['#2196F3']}
tintColor="#2196F3"
/>
}
onEndReached={onEndReached}
onEndReachedThreshold={0.5}
removeClippedSubviews={true}
maxToRenderPerBatch={10}
windowSize={10}
initialNumToRender={10}
getItemLayout={(data, index) => ({
length: 80,
offset: 80 * index,
index,
})}
style={styles.container}
/>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f5f5f5',
},
header: {
backgroundColor: '#2196F3',
padding: 20,
alignItems: 'center',
},
headerTitle: {
fontSize: 24,
fontWeight: 'bold',
color: 'white',
marginBottom: 5,
},
headerSubtitle: {
fontSize: 16,
color: 'rgba(255, 255, 255, 0.8)',
},
item: {
backgroundColor: 'white',
padding: 15,
marginHorizontal: 10,
marginVertical: 5,
borderRadius: 8,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 3,
},
itemTitle: {
fontSize: 18,
fontWeight: 'bold',
color: '#333',
marginBottom: 5,
},
itemSubtitle: {
fontSize: 14,
color: '#666',
marginBottom: 5,
},
itemTimestamp: {
fontSize: 12,
color: '#999',
},
footer: {
padding: 20,
alignItems: 'center',
},
footerText: {
marginTop: 10,
fontSize: 14,
color: '#666',
},
empty: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 40,
},
emptyText: {
fontSize: 16,
color: '#999',
},
});
export default AdvancedFlatList;Test your understanding of this topic:
Learn to use Modal and Alert components for displaying overlays and user interactions in React Native applications
Content by: Ronak Macwan
React Native Developer
Modal component provides a way to present content above an enclosing view. It's perfect for overlays, popups, and full-screen presentations.
Alert displays an alert dialog with a title, message, and buttons. It's useful for confirming actions or displaying important information.
import React, { useState } from 'react';
import {
View,
Text,
TouchableOpacity,
Modal,
StyleSheet,
Alert,
TextInput,
ScrollView
} from 'react-native';
const ModalAlertExample = () => {
const [modalVisible, setModalVisible] = useState(false);
const [formData, setFormData] = useState({
name: '',
email: '',
message: '',
});
const showAlert = () => {
Alert.alert(
'Confirmation',
'Are you sure you want to proceed?',
[
{
text: 'Cancel',
onPress: () => console.log('Cancel Pressed'),
style: 'cancel',
},
{
text: 'OK',
onPress: () => console.log('OK Pressed'),
},
],
{ cancelable: false }
);
};
const showPrompt = () => {
Alert.prompt(
'Enter Name',
'Please enter your name:',
[
{
text: 'Cancel',
onPress: () => console.log('Cancel Pressed'),
style: 'cancel',
},
{
text: 'OK',
onPress: (name) => {
console.log('Name entered:', name);
setFormData(prev => ({ ...prev, name: name || '' }));
},
},
],
'plain-text',
'John Doe'
);
};
const showActionSheet = () => {
Alert.alert(
'Choose Option',
'Select an option from the list:',
[
{
text: 'Option 1',
onPress: () => console.log('Option 1 selected'),
},
{
text: 'Option 2',
onPress: () => console.log('Option 2 selected'),
},
{
text: 'Option 3',
onPress: () => console.log('Option 3 selected'),
},
{
text: 'Cancel',
onPress: () => console.log('Cancel Pressed'),
style: 'cancel',
},
],
{ cancelable: true }
);
};
const handleSubmit = () => {
if (!formData.name || !formData.email) {
Alert.alert('Error', 'Please fill in all required fields');
return;
}
Alert.alert(
'Success',
'Form submitted successfully!',
[
{
text: 'OK',
onPress: () => {
setModalVisible(false);
setFormData({ name: '', email: '', message: '' });
},
},
]
);
};
return (
<ScrollView style={styles.container}>
<Text style={styles.title}>Modal & Alert Examples</Text>
<View style={styles.buttonContainer}>
<TouchableOpacity style={styles.button} onPress={showAlert}>
<Text style={styles.buttonText}>Show Alert</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.button} onPress={showPrompt}>
<Text style={styles.buttonText}>Show Prompt</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.button} onPress={showActionSheet}>
<Text style={styles.buttonText}>Show Action Sheet</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.button, styles.modalButton]}
onPress={() => setModalVisible(true)}
>
<Text style={styles.buttonText}>Open Modal</Text>
</TouchableOpacity>
</View>
<Modal
animationType="slide"
transparent={true}
visible={modalVisible}
onRequestClose={() => setModalVisible(false)}
>
<View style={styles.modalOverlay}>
<View style={styles.modalContent}>
<Text style={styles.modalTitle}>Contact Form</Text>
<TextInput
style={styles.input}
placeholder="Name"
value={formData.name}
onChangeText={(text) => setFormData(prev => ({ ...prev, name: text }))}
/>
<TextInput
style={styles.input}
placeholder="Email"
value={formData.email}
onChangeText={(text) => setFormData(prev => ({ ...prev, email: text }))}
keyboardType="email-address"
/>
<TextInput
style={[styles.input, styles.textArea]}
placeholder="Message"
value={formData.message}
onChangeText={(text) => setFormData(prev => ({ ...prev, message: text }))}
multiline
numberOfLines={4}
/>
<View style={styles.modalButtons}>
<TouchableOpacity
style={[styles.modalButton, styles.cancelButton]}
onPress={() => setModalVisible(false)}
>
<Text style={styles.modalButtonText}>Cancel</Text>
</TouchableOpacity>
<TouchableOpacity
style={[styles.modalButton, styles.submitButton]}
onPress={handleSubmit}
>
<Text style={styles.modalButtonText}>Submit</Text>
</TouchableOpacity>
</View>
</View>
</View>
</Modal>
</ScrollView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 20,
backgroundColor: '#f5f5f5',
},
title: {
fontSize: 24,
fontWeight: 'bold',
textAlign: 'center',
marginBottom: 30,
color: '#333',
},
buttonContainer: {
gap: 15,
},
button: {
backgroundColor: '#2196F3',
padding: 15,
borderRadius: 8,
alignItems: 'center',
},
modalButton: {
backgroundColor: '#4CAF50',
},
buttonText: {
color: 'white',
fontSize: 16,
fontWeight: 'bold',
},
modalOverlay: {
flex: 1,
backgroundColor: 'rgba(0, 0, 0, 0.5)',
justifyContent: 'center',
alignItems: 'center',
},
modalContent: {
backgroundColor: 'white',
padding: 20,
borderRadius: 10,
width: '90%',
maxHeight: '80%',
},
modalTitle: {
fontSize: 20,
fontWeight: 'bold',
textAlign: 'center',
marginBottom: 20,
color: '#333',
},
input: {
borderWidth: 1,
borderColor: '#ddd',
borderRadius: 8,
padding: 12,
marginBottom: 15,
fontSize: 16,
},
textArea: {
height: 100,
textAlignVertical: 'top',
},
modalButtons: {
flexDirection: 'row',
justifyContent: 'space-between',
marginTop: 20,
},
cancelButton: {
backgroundColor: '#f44336',
flex: 1,
marginRight: 10,
},
submitButton: {
backgroundColor: '#4CAF50',
flex: 1,
marginLeft: 10,
},
modalButtonText: {
color: 'white',
fontSize: 16,
fontWeight: 'bold',
textAlign: 'center',
},
});
export default ModalAlertExample;Test your understanding of this topic:
Learn to create reusable custom components in React Native with proper styling, props, and state management
Content by: Ronak Macwan
React Native Developer
Design reusable components with clear prop interfaces, proper TypeScript types, and consistent styling patterns.
Use composition patterns to build complex UI elements from smaller, focused components.
import React from 'react';
import { TouchableOpacity, Text, StyleSheet, ViewStyle, TextStyle, ActivityIndicator } from 'react-native';
interface CustomButtonProps {
title: string;
onPress: () => void;
variant?: 'primary' | 'secondary' | 'outline' | 'danger';
size?: 'small' | 'medium' | 'large';
disabled?: boolean;
loading?: boolean;
icon?: React.ReactNode;
style?: ViewStyle;
textStyle?: TextStyle;
}
const CustomButton: React.FC<CustomButtonProps> = ({
title,
onPress,
variant = 'primary',
size = 'medium',
disabled = false,
loading = false,
icon,
style,
textStyle,
}) => {
const getButtonStyle = (): ViewStyle => {
const baseStyle = [styles.button, styles[`button${size.charAt(0).toUpperCase() + size.slice(1)}`]];
switch (variant) {
case 'primary':
baseStyle.push(styles.primaryButton);
break;
case 'secondary':
baseStyle.push(styles.secondaryButton);
break;
case 'outline':
baseStyle.push(styles.outlineButton);
break;
case 'danger':
baseStyle.push(styles.dangerButton);
break;
}
if (disabled) {
baseStyle.push(styles.disabledButton);
}
return StyleSheet.flatten([baseStyle, style]);
};
const getTextStyle = (): TextStyle => {
const baseStyle = [styles.text, styles[`text${size.charAt(0).toUpperCase() + size.slice(1)}`]];
switch (variant) {
case 'primary':
baseStyle.push(styles.primaryText);
break;
case 'secondary':
baseStyle.push(styles.secondaryText);
break;
case 'outline':
baseStyle.push(styles.outlineText);
break;
case 'danger':
baseStyle.push(styles.dangerText);
break;
}
if (disabled) {
baseStyle.push(styles.disabledText);
}
return StyleSheet.flatten([baseStyle, textStyle]);
};
return (
<TouchableOpacity
style={getButtonStyle()}
onPress={onPress}
disabled={disabled || loading}
activeOpacity={0.7}
>
{loading ? (
<ActivityIndicator
size="small"
color={variant === 'outline' ? '#2196F3' : 'white'}
/>
) : (
<>
{icon && <>{icon}</>}
<Text style={getTextStyle()}>{title}</Text>
</>
)}
</TouchableOpacity>
);
};
const styles = StyleSheet.create({
button: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
borderRadius: 8,
paddingHorizontal: 16,
paddingVertical: 12,
},
buttonSmall: {
paddingHorizontal: 12,
paddingVertical: 8,
},
buttonMedium: {
paddingHorizontal: 16,
paddingVertical: 12,
},
buttonLarge: {
paddingHorizontal: 24,
paddingVertical: 16,
},
primaryButton: {
backgroundColor: '#2196F3',
},
secondaryButton: {
backgroundColor: '#6c757d',
},
outlineButton: {
backgroundColor: 'transparent',
borderWidth: 1,
borderColor: '#2196F3',
},
dangerButton: {
backgroundColor: '#dc3545',
},
disabledButton: {
backgroundColor: '#e9ecef',
borderColor: '#e9ecef',
},
text: {
fontWeight: '600',
textAlign: 'center',
},
textSmall: {
fontSize: 14,
},
textMedium: {
fontSize: 16,
},
textLarge: {
fontSize: 18,
},
primaryText: {
color: 'white',
},
secondaryText: {
color: 'white',
},
outlineText: {
color: '#2196F3',
},
dangerText: {
color: 'white',
},
disabledText: {
color: '#6c757d',
},
});
export default CustomButton;import React from 'react';
import { View, Text, StyleSheet, ViewStyle, TouchableOpacity } from 'react-native';
interface CustomCardProps {
title?: string;
subtitle?: string;
children?: React.ReactNode;
onPress?: () => void;
style?: ViewStyle;
elevation?: number;
padding?: number;
margin?: number;
borderRadius?: number;
backgroundColor?: string;
}
const CustomCard: React.FC<CustomCardProps> = ({
title,
subtitle,
children,
onPress,
style,
elevation = 2,
padding = 16,
margin = 8,
borderRadius = 8,
backgroundColor = 'white',
}) => {
const cardStyle = {
padding,
margin,
borderRadius,
backgroundColor,
shadowColor: '#000',
shadowOffset: { width: 0, height: elevation },
shadowOpacity: 0.1,
shadowRadius: elevation * 2,
elevation,
};
const CardContent = () => (
<View style={[cardStyle, style]}>
{title && <Text style={styles.title}>{title}</Text>}
{subtitle && <Text style={styles.subtitle}>{subtitle}</Text>}
{children}
</View>
);
if (onPress) {
return (
<TouchableOpacity onPress={onPress} activeOpacity={0.7}>
<CardContent />
</TouchableOpacity>
);
}
return <CardContent />;
};
const styles = StyleSheet.create({
title: {
fontSize: 18,
fontWeight: 'bold',
color: '#333',
marginBottom: 8,
},
subtitle: {
fontSize: 14,
color: '#666',
marginBottom: 12,
},
});
export default CustomCard;Test your understanding of this topic:
Implement smooth animations in React Native using built-in Animated API and popular animation libraries
Content by: Ronak Macwan
React Native Developer
Use React Native's built-in Animated API for creating smooth, performant animations with timing, spring, and decay animations.
Explore libraries like react-native-reanimated and lottie-react-native for advanced animations and complex motion graphics.
import React, { useRef, useEffect } from 'react';
import { View, Text, StyleSheet, Animated, TouchableOpacity, Dimensions } from 'react-native';
const { width, height } = Dimensions.get('window');
const AdvancedAnimationExample = () => {
// Animation values
const fadeAnim = useRef(new Animated.Value(0)).current;
const scaleAnim = useRef(new Animated.Value(0.5)).current;
const translateX = useRef(new Animated.Value(-width)).current;
const rotateAnim = useRef(new Animated.Value(0)).current;
const bounceAnim = useRef(new Animated.Value(0)).current;
useEffect(() => {
// Initial animation sequence
Animated.sequence([
Animated.timing(fadeAnim, {
toValue: 1,
duration: 1000,
useNativeDriver: true,
}),
Animated.parallel([
Animated.spring(scaleAnim, {
toValue: 1,
tension: 50,
friction: 3,
useNativeDriver: true,
}),
Animated.timing(translateX, {
toValue: 0,
duration: 800,
useNativeDriver: true,
}),
]),
]).start();
}, []);
const startRotation = () => {
Animated.loop(
Animated.timing(rotateAnim, {
toValue: 1,
duration: 2000,
useNativeDriver: true,
})
).start();
};
const startBounce = () => {
Animated.sequence([
Animated.timing(bounceAnim, {
toValue: 1,
duration: 300,
useNativeDriver: true,
}),
Animated.timing(bounceAnim, {
toValue: 0,
duration: 300,
useNativeDriver: true,
}),
]).start();
};
const startPulse = () => {
Animated.loop(
Animated.sequence([
Animated.timing(scaleAnim, {
toValue: 1.2,
duration: 500,
useNativeDriver: true,
}),
Animated.timing(scaleAnim, {
toValue: 1,
duration: 500,
useNativeDriver: true,
}),
])
).start();
};
const resetAnimations = () => {
fadeAnim.setValue(0);
scaleAnim.setValue(0.5);
translateX.setValue(-width);
rotateAnim.setValue(0);
bounceAnim.setValue(0);
};
const rotateInterpolate = rotateAnim.interpolate({
inputRange: [0, 1],
outputRange: ['0deg', '360deg'],
});
const bounceInterpolate = bounceAnim.interpolate({
inputRange: [0, 1],
outputRange: [0, -20],
});
return (
<View style={styles.container}>
<Text style={styles.title}>Advanced Animations</Text>
<View style={styles.animationContainer}>
{/* Fade Animation */}
<Animated.View
style={[
styles.animationBox,
styles.fadeBox,
{
opacity: fadeAnim,
},
]}
>
<Text style={styles.boxText}>Fade In</Text>
</Animated.View>
{/* Scale Animation */}
<Animated.View
style={[
styles.animationBox,
styles.scaleBox,
{
transform: [{ scale: scaleAnim }],
},
]}
>
<Text style={styles.boxText}>Scale</Text>
</Animated.View>
{/* Translate Animation */}
<Animated.View
style={[
styles.animationBox,
styles.translateBox,
{
transform: [{ translateX }],
},
]}
>
<Text style={styles.boxText}>Slide In</Text>
</Animated.View>
{/* Rotation Animation */}
<Animated.View
style={[
styles.animationBox,
styles.rotateBox,
{
transform: [{ rotate: rotateInterpolate }],
},
]}
>
<Text style={styles.boxText}>Rotate</Text>
</Animated.View>
{/* Bounce Animation */}
<Animated.View
style={[
styles.animationBox,
styles.bounceBox,
{
transform: [{ translateY: bounceInterpolate }],
},
]}
>
<Text style={styles.boxText}>Bounce</Text>
</Animated.View>
</View>
<View style={styles.buttonContainer}>
<TouchableOpacity style={styles.button} onPress={startRotation}>
<Text style={styles.buttonText}>Start Rotation</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.button} onPress={startBounce}>
<Text style={styles.buttonText}>Bounce</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.button} onPress={startPulse}>
<Text style={styles.buttonText}>Pulse</Text>
</TouchableOpacity>
<TouchableOpacity style={[styles.button, styles.resetButton]} onPress={resetAnimations}>
<Text style={styles.buttonText}>Reset</Text>
</TouchableOpacity>
</View>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 20,
backgroundColor: '#f5f5f5',
},
title: {
fontSize: 24,
fontWeight: 'bold',
textAlign: 'center',
marginBottom: 30,
color: '#333',
},
animationContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
gap: 20,
},
animationBox: {
width: 100,
height: 100,
justifyContent: 'center',
alignItems: 'center',
borderRadius: 10,
},
fadeBox: {
backgroundColor: '#2196F3',
},
scaleBox: {
backgroundColor: '#4CAF50',
},
translateBox: {
backgroundColor: '#FF9800',
},
rotateBox: {
backgroundColor: '#9C27B0',
},
bounceBox: {
backgroundColor: '#F44336',
},
boxText: {
color: 'white',
fontWeight: 'bold',
fontSize: 12,
},
buttonContainer: {
flexDirection: 'row',
flexWrap: 'wrap',
justifyContent: 'center',
gap: 10,
marginTop: 20,
},
button: {
backgroundColor: '#2196F3',
paddingHorizontal: 20,
paddingVertical: 10,
borderRadius: 5,
},
resetButton: {
backgroundColor: '#f44336',
},
buttonText: {
color: 'white',
fontWeight: 'bold',
},
});
export default AdvancedAnimationExample;Test your understanding of this topic:
Continue your learning journey and master the next set of concepts.
Continue to Module 9