This Repo is sourced from the YouTube Channel Codevolution Playlist Doc
React Native is an open-source framework for building native Android and iOS applications using React.
- JavaScript to access platform-specific APIs (such as camera and push notifications)
- React components to define the appearance and behavior of your user interface
- React itself is a library designed for building user interfaces
react-domfor web apps andreact-nativefor native mobile apps
- JavaScript
- React fundamentals
- iOS apps require Swift or Objective-C.
- Android apps require Java or Kotlin.
- With approximately 31% on iOS and 68% on Android, you would typically need to develop two separate apps using different technologies.
- With React Native, you can create an app that works seamlessly on both platforms.
React Native is in high demand in the job market.
Rather than assembling separate teams of iOS and Android developers, businesses can opt for a single team proficient in React Native, saving time and costs.
If the team is well-versed in React, their expertise can extend to web app development as well.
https://github.com/gopinav/React-Native-Tutorials
- React Native is an open-source framework maintained by Meta (Facebook), designed for building cross-platform apps.
- Expo is an open-source framework maintained by Expo itself, designed for building cross-platform apps.
- Unlike plain RN, which can be challenging and time-consuming to set up, Expo provides a streamlined experience.
- Expo has a suite of tools and services built around React Native, which greatly simplifies the development process.
- With plain React Native, you cannot build iOS apps on a Windows or Linux machine. Native iOS apps can only be built on a macOS machine.
- With Expo, you can develop your app on Windows or Linux and run your app on a physical iPhone.
- Expo has evolved significantly over the years and now supports nearly all the features necessary for building mobile apps
- In the event that you require access to native Android or iOS code, Expo allows you to eject your app and work with a plain React Native codebase
https://reactnative.dev/docs/environment-setup
React Native offers a collection of essential pre-built components known as Core Components, which are readily available for building your native app's user interface.
Below is a comparison of React Native's core UI components with their counterparts in Android, iOS, and Web:
| React Native UI Component | Android View | iOS View | Web Analog |
|---|---|---|---|
<View> |
<ViewGroup> |
<UIView> |
A non-scrolling <div> |
<Text> |
<TextView> |
<UITextView> |
<p> |
<Image> |
<ImageView> |
<UIImageView> |
<img> |
<ScrollView> |
<ScrollView> |
<UIScrollView> |
<div> |
<TextInput> |
<EditText> |
<UITextField> |
<input type="text"> |
- Acts as a container for other components.
- Similar to
<div>in web development. - Supports layout, styling, and touch handling.
- Used for displaying text.
- Supports styling, nesting, and accessibility features.
- Unlike HTML,
<Text>components must be wrapped inside another<Text>for nesting.
- Displays images from a local or remote source.
- Uses
<Image>component instead of<img>. - Supports various formats and optimization options.
- Used to create scrollable views.
- Renders all child components at once (unlike
FlatListwhich supports lazy loading). - Supports both horizontal and vertical scrolling.
- Allows user input, like an input field in HTML.
- Supports features like keyboard type, auto-correction, and secure text entry (for passwords).
- Cross-platform compatibility: Write once, run on both Android and iOS.
- Performance: Uses native views for efficient rendering.
- Ease of development: Provides built-in UI components similar to web and native elements.
React Native’s core components simplify the development process by offering pre-built, optimized UI elements that function seamlessly across platforms.
The View component is a fundamental core component in React Native that serves as a building block for creating user interfaces.
It functions as a container that supports layout using Flexbox, styling, touch handling, and accessibility controls.
In web development terms, the View component can be compared to the <div> tag.
The View component is typically nested inside other views and can have zero or more children of any type.
import { View, Text } from "react-native";<View
style={{
width: 200,
height: 100,
backgroundColor: "#peach",
}}
>
<Text style={{ color: "#fff", fontSize: 18 }}>Hello, View!</Text>
</View><View
style={{
padding: 20,
backgroundColor: "#f5f5f5",
}}
>
<View
style={{
width: 100,
height: 100,
backgroundColor: "#e74c3c",
}}
>
<Text style={{ color: "#fff" }}>Box 1</Text>
</View>
<View
style={{
width: 100,
height: 100,
backgroundColor: "#2ecc71",
}}
>
<Text style={{ color: "#fff" }}>Box 2</Text>
</View>
</View>The Text component is used for displaying text.
- It supports nesting, styling, and touch handling.
In web development terms, the Text component can be compared to the <p> tag.
Depending on the target platform, React Native will translate this component to:
UITextView(iOS)TextView(Android)<p>(Web)
import { Text, View } from "react-native";<Text>Hello, React Native!</Text><Text style={{ fontSize: 20, fontWeight: "bold", color: "#2c3e50" }}>
Styled Text
</Text><Text style={{ fontSize: 18 }}>
This is <Text style={{ fontWeight: "bold" }}> nested </Text> text.
</Text><Text style={{ color: "red" }}>Red Text</Text>
<Text style={{ color: "green" }}>Green Text</Text>
<Text style={{ color: "blue" }}>Blue Text</Text>The Image component enables us to display various types of images, including:
- Static images
- Network images
- Images from the local disk, such as the camera roll
Image supports styling, nesting, and accessibility features.
React Native seamlessly translates the Image component to platform-specific counterparts:
UIImageView(iOS)ImageView(Android)<img>(Web)
import { Image, ImageBackground, Text } from "react-native";
const logoImg = require("./assets/adaptive-icon.png");<Image source={logoImg} style={{ width: 200, height: 200 }} /><Image
source={{ uri: "https://picsum.photos/300" }}
style={{ width: 200, height: 200 }}
/><ImageBackground source={logoImg} style={{ flex: 1 }}>
<Text>This is a Background Image</Text>
</ImageBackground>The ScrollView component wraps the platform-specific scrolling functionality.
It supports horizontal scrolling, vertical scrolling, and nested scrolling.
Note: ScrollViews require a bounded height to function properly.
import { View, ScrollView, Text } from "react-native";const logoImg = require("./assets/adaptive-icon.png");
export default function App() {
return (
<View style={{ flex: 1, backgroundColor: "plum", padding: 20 }}>
<ScrollView>
<Text>
This is a scrollable view. Add more content here to make it
scrollable.
</Text>
</ScrollView>
</View>
);
}The Button component allows users to trigger actions.
Button component requires a title and an onPress event handler. The onPress event handler is called when the button is pressed.
Note: The Button component has platform-specific rendering for iOS and Android.
import { Button } from "react-native";<Button title="Click Me" /><Button
title="Click Me"
onPress={() => {
console.log("Button Pressed");
}}<Button
title="Click Me"
onPress={() => {
console.log("Button Pressed");
}}
color="midnightblue"
/><Button
title="Click Me"
onPress={() => {
console.log("Button Pressed");
}}
disabled
/>}}
Pressable is a wrapper component that detects various stages of press interactions on its defined children.
Pressable component is a more flexible alternative to the Button component.
Pressable allows you to apply custom styles to the button.
onPressInis called when a press is activated.onLongPressis triggered when a press is held for longer than 500 milliseconds.onPressOutis called when the press gesture is deactivated.
import { Image, Text, Pressable } from "react-native";<Pressable>
<Text>Press Me</Text>
</Pressable><Pressable onPress={() => console.log("Image Pressed")}>
<Image
source={{ uri: "https://picsum.photos/300" }}
style={{ width: 200, height: 200 }}
/>
</Pressable>
<Pressable onPress={() => console.log("Text Pressed")}>
<Text>Hello, Press Here!</Text>{" "}
</Pressable>Modal is a screen that overlays the app content to provide important information or prompt the user for a decision.
Since they are purposefully interruptive, make sure you use them only when necessary.
import { useState } from "react";
import { View, Button, Image, Text, Pressable, Modal } from "react-native";export default function App() {
const [isModalVisible, setIsModalVisible] = useState(false);
return (
<View style={{ flex: 1, backgroundColor: "plum", padding: 20 }}>
<Button
title="Press"
onPress={() => setIsModalVisible(true)}
color="midnightblue"
/>
<Modal
visible={isModalVisible}
onRequestClose={() => setIsModalVisible(false)}
animationType="slide"
>
<View
style={{ flex: 1, backgroundColor: "lightblue", padding: 60 }}
>
<Text>Modal Content</Text>
<Button
title="Close"
onPress={() => setIsModalVisible(false)}
color="midnightblue"
/>
</View>
</Modal>
</View>
);
}The StatusBar component allows you to control the application's status bar.
The status bar is the zone, typically at the top of the screen, that displays the current time, Wi-Fi and network information, battery level, and other status icons.
This example shows how to change the status bar style and background color using the StatusBar component in React Native.
import { useState } from "react";import { View, StatusBar } from "react-native";
export default function App() {
return (
<View style={{ flex: 1, backgroundColor: "plum", padding: 20 }}>
<StatusBar
backgroundColor="lightgreen" // Set the background color
barStyle={"dark-content"} // Set the status bar style
hidden // Hide the status bar
/>
</View>
);
}The ActivityIndicator component displays a circular loading indicator.
It is used to inform users about the status of ongoing processes, such as loading an app, submitting a form, or saving updates.
import { ActivityIndicator } from "react-native";export default function App() {
return (
<View style={{ flex: 1, backgroundColor: "plum", padding: 20 }}>
<ActivityIndicator /> // Default
<ActivityIndicator size={"large"} /> // change size
<ActivityIndicator size={"large"} color="purple" /> // change color
<ActivityIndicator
size={"large"}
color="purple"
animating={false}
/> // stop animation
</View>
);
}Alert launches an alert dialog with specified title and message.
Optionally, you can also specify a list of buttons.
import { View, Alert, Button } from "react-native";export default function App() {
return (
<View style={{ flex: 1, backgroundColor: "plum", padding: 20 }}>
<Button
title="Alert"
onPress={() => Alert.alert("Invalid Password!")}
/>
</View>
);
}export default function App() {
return (
<View style={{ flex: 1, backgroundColor: "plum", padding: 20 }}>
<Button
title="Alert2"
onPress={() => Alert.alert("Invalid Password!", "Try Again!")}
/>
</View>
);
}export default function App() {
return (
<View style={{ flex: 1, backgroundColor: "plum", padding: 20 }}>
<Button
title="Alert3"
onPress={() =>
Alert.alert("Invalid Password!", "Try Again!", [
{
text: "Cancel",
onPress: () => console.log("User Pressed Canceled"),
},
{ text: "OK", onPress: () => console.log("User Pressed OK") },
])
}
/>
</View>
);
}Custom components are reusable UI elements that can be created using React Native's core components.
import { View, Text } from "react-native";
export default function Greet({ name }) {
return (
<View>
<Text>Hello, {name} Welcome Back!!!</Text>
</View>
);
}import { View } from "react-native";
import Greet from "./Components/Greet";
export default function App() {
return (
<View style={{ flex: 1, backgroundColor: "plum", padding: 60 }}>
<Greet name="Uday Dey" />
<Greet name="John Doe" />
</View>
);
}- React Native does not use CSS for styling
- You style your app using JavaScript
- Names are written in camel case
Example:
backgroundColor instead of background-color
- Inline styles
- StyleSheet API
Inline styles are applied directly to the component using the style prop.
import { View } from "react-native";
export default function App() {
return (
<View style={{ flex: 1, backgroundColor: "plum", padding: 60 }}>
<Text style={{ color: "blue" }}>Using Inline styles</Text>
</View>
);
}StyleSheet API is used to create styles for components.
import { View, Text, StyleSheet } from "react-native";
export default function App() {
return (
<View style={styles.container}>
<Text style={styles.title}>Using StyleSheet API</Text>
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: "plum", padding: 60 },
title: { color: "blue" },
});- StyleSheet API
- Style various core components
- Styling across iOS and Android
import { View, Text, StyleSheet } from "react-native";
export default function App() {
return (
<View style={styles.container}>
<View style={[styles.box, styles.lightblueBoxBG]}>
<Text>This is a Blue Box</Text>
</View>
<View style={[styles.box, styles.lightgreenBoxBG]}>
<Text>This is a Green Box</Text>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: "plum", padding: 60 },
box: {
width: 200,
height: 200,
padding: 60,
},
lightblueBoxBG: {
backgroundColor: "lightblue",
},
lightgreenBoxBG: {
backgroundColor: "lightgreen",
},
});The box model is a fundamental concept in CSS that defines the layout and spacing of elements.
In React Native, the box model is used to position and style elements in the user interface.
Elements of box model are:
ContentPaddingBorderMargin
import { View, Text, StyleSheet } from "react-native";export default function App() {
return (
<View style={styles.container}>
<View style={[styles.box, styles.lightblueBoxBG]}>
<Text>This is a Blue Box</Text>
</View>
<View style={[styles.box, styles.lightgreenBoxBG]}>
<Text>This is a Green Box</Text>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: "plum", padding: 60 },
box: {
width: 200,
height: 200,
// padding: 60,
paddingHorizontal: 20,
paddingVertical: 20,
marginVertical: 10,
borderWidth: 2,
borderColor: "red",
borderRadius: 10,
},
lightblueBoxBG: {
backgroundColor: "lightblue",
justifyContent: "center",
alignItems: "center",
},
lightgreenBoxBG: {
backgroundColor: "lightgreen",
justifyContent: "center",
alignItems: "center",
},
});There are 2 types ways to apply shadow:
box-shadowfor iOSelevationfor Android
The box-shadow property is used to add a shadow to a view.
Properties:
offset-xoffset-yblur-radiusspread-radiuscolor
import { View, Text, StyleSheet } from "react-native";
export default function App() {
return (
<View style={styles.container}>
<View style={[styles.box, styles.lightblueBoxBG, styles.boxShadow]}>
<Text>This is a Blue Box</Text>
</View>
<Text>This is a Green Box</Text>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: "plum", padding: 60 },
box: {
width: 200,
height: 200,
paddingHorizontal: 20,
paddingVertical: 20,
marginVertical: 10,
borderWidth: 2,
borderColor: "red",
borderRadius: 10,
},
lightblueBoxBG: {
backgroundColor: "lightblue",
justifyContent: "center",
alignItems: "center",
},
boxShadow: {
shadowColor: "black",
shadowOffset: { width: 6, height: 6 },
shadowOpacity: 0.6,
shadowRadius: 4,
},
});The elevation property is used to add a shadow to a view.
Properties:
elevationshadow-color
import { View, Text, StyleSheet } from "react-native";
export default function App() {
return (
<View style={styles.container}>
<View
style={[styles.box, styles.lightgreenBoxBG, styles.androidShadow]}
>
<Text>This is a Green Box</Text>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: "plum", padding: 60 },
box: {
width: 200,
height: 200,
// padding: 60,
paddingHorizontal: 20,
paddingVertical: 20,
marginVertical: 10,
borderWidth: 2,
borderColor: "red",
borderRadius: 10,
},
lightgreenBoxBG: {
backgroundColor: "lightgreen",
justifyContent: "center",
alignItems: "center",
},
androidShadow: {
elevation: 10,
shadowColor: "darkblue",
},
});At the core of layout design in React Native is Flexbox
Flexbox is a powerful one-dimensional layout model used to arrange elements within a container
With Flexbox, you have the freedom to arrange items either horizontally (from left to right or right to left) or vertically (from top to bottom or bottom to top)
You can easily control the spacing and alignment of items within the container
Flexbox consists of two main entities:
- flex container
- flex items
<View>
<View>Item 1</View>
<View>Item 2</View>
<View>Item 3</View>
</View>Much like learning CSS
Familiarize yourself with the various Flexbox properties and understand how they function
Once you grasp the concepts of Flexbox, you'll have a solid understanding of how layouts are created in React Native
The flex property plays a crucial role in defining how much of a view will fill the screen along the main axis.
It accepts an integer value greater than or equal to 0, indicating the fraction of the available space the component should occupy.
<Box style={{ backgroundColor: "blue", flex: 1 }}>Box 1</Box>
<Box style={{ backgroundColor: "green", flex: 3 }}>Box 2</Box>The flexDirection property establishes the main axis, which in turn determines how the flex items are placed within the container.
By default, the main axis flows from top to bottom, causing the items to be displayed from top to bottom in the UI.
By changing the value of the flexDirection property, we can alter how the items are positioned.
Properties:
column: defaultcolumn-reverserowrow-reverse
const styles = StyleSheet.create({
container: {
flexDirection: "row",
},
});The justifyContent property controls how flex items are positioned along the main axis.
Properties:
flex-startflex-endcenterspace-betweenspace-aroundspace-evenly
const styles = StyleSheet.create({
container: {
justifyContent: "space-between",
},
});alignItems defines the default behavior for laying out
flex items along the container's cross-axis
Works similarly to justifyContent, but in the
perpendicular direction
Properties:
- stretch
- center
- flex-start
- flex-end
- baseline
baseline is the default value
const styles = StyleSheet.create({
container: {
alignItems: "center",
},
});alignItems is applied to the container and controls the alignment of all items within it.
alignSelf is applied to individual items, allowing us to control the alignment of each item independently.
The values available for alignSelf are similar to those used in alignItems.
export default function App() {
return (
<View style={styles.container}>
<Box style={{ backgroundColor: "blue", alignSelf: "flex-start" }}>
Box 1
</Box>
<Box style={{ backgroundColor: "green", alignSelf: "flex-end" }}>
Box 2
</Box>
<Box style={{ backgroundColor: "yellow", alignSelf: "center" }}>
Box 3
</Box>
<Box style={{ backgroundColor: "purple", alignSelf: "stretch" }}>
Box 4
</Box>
<Box style={{ backgroundColor: "orange", alignSelf: "baseline" }}>
Box 5
</Box>
<Box style={{ backgroundColor: "pink", alignSelf: "auto" }}>Box 6</Box>
<Box style={{ backgroundColor: "brown" }}>Box 7</Box>
<Box style={{ backgroundColor: "cyan" }}>Box 8</Box>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: "stretch",
marginTop: 64,
borderWidth: 6,
borderColor: "red",
},
});Flexbox allows items to wrap to the next line if there is not enough space to fit them all in the container
Properties:
nowrap: defaultwrap: wrap items to the next linewrap-reverse: wrap items to the previous line
export default function App() {
return (
<View style={styles.container}>
<Box style={{ backgroundColor: "blue" }}>Box 1</Box>
<Box style={{ backgroundColor: "green" }}>Box 2</Box>
<Box style={{ backgroundColor: "yellow" }}>Box 3</Box>
<Box style={{ backgroundColor: "purple" }}>Box 4</Box>
<Box style={{ backgroundColor: "orange" }}>Box 5</Box>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: "row",
flexWrap: "wrap",
marginTop: 64,
borderWidth: 6,
borderColor: "red",
},
});The alignContent property controls how flex items are positioned along the cross axis.
Similar to how the alignItems property aligns individual items along the cross-axis
A very important condition is that multiple columns or rows must exist within the container
Properties:
flex-startflex-endcenterspace-betweenspace-aroundstretch
const styles = StyleSheet.create({
container: {
alignContent: "stretch",
},
});Gap related properties allow us to manage spacing between rows and columns
Properties:
rowGap: gap between rowscolumnGap: gap between columnsgap: gap between rows and columns
rowGap: 20,
columnGap: 10,The flexBasis property determines the initial size of a flex item before any extra space in the container is distributed
Alternative to using the height and width properties in flex layouts
Properties:
auto: defaultcontent: size based on contentmin-content: minimum size based on contentmax-content: maximum size based on contentfit-content: size based on content plus padding
<Box style={{ backgroundColor: "orange", flexBasis: 170 }}>Box 5</Box>The flexShrink property controls how flex items shrink to fit the container
Properties:
0: default123- and so on
<Box style={{ backgroundColor: "orange", flexShrink: 2 }}>Box 5</Box>flexGrow determines how much space an item should occupy inside a flex container when there is extra space available.
The flexGrow factor is always relative to other items within the container.
Properties:
0: default123- and so on
<Box style={{ backgroundColor: "orange", flexGrow: 2 }}>Box 5</Box>When flex is set to a positive number, it is equivalent to setting flexGrow with the same positive number.
flex also implicitly sets flexShrink to 1 and flexBasis to 0.
flex: <positive number>
flexGrow: <positive number>, flexShrink: 1, flexBasis: 0The layouts are based on the position property, which defines how an element is positioned within its parent container:
relativeabsolute
In this layout, an element is positioned according to the normal flow of the layout.
It remains in its original position and can be offset from that position using the top, right, bottom, and left values.
Importantly, this offset does not affect the positioning of any sibling or parent elements.
<Box style={{ backgroundColor: "blue", top: 100, left: 100 }}>Box 1</Box>In this layout, an element does not participate in the normal flow of the layout.
It is instead laid out independently of its siblings.
The position of the element is determined by the top, right, bottom, and left values, which specify specific coordinates relative to its parent container.
<Box
style={{
backgroundColor: "orange",
position: "absolute",
top: 300,
left: 200,
}}
>
Box 2
</Box>- Currently, all our learning has centered around iPhone 14 and Pixel 4 devices
- Our app's users won’t all be using identical devices
- Device sizes may vary, ranging from more compact phones to larger devices like iPads or Android tablets
- We must ensure that our app’s user interface remains responsive to these different device sizes while maintaining an optimal user experience
- On the same device, a user might opt for portrait mode, while another prefers landscape orientation
- A dynamic API is an API that is responsive to the device screen size and orientation
- A dynamic API is an API that adjusts its behavior based on the device's screen size and orientation
import { StyleSheet, Text, View, Dimensions } from "react-native";
export default function App() {
return (
<View style={styles.container}>
<View style={styles.box}>
<Text style={styles.text}>Welcome!</Text>
</View>
</View>
);
}
const windowWidth = Dimensions.get("window").width;
const windowHeight = Dimensions.get("window").height;
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "plum",
alignItems: "center",
justifyContent: "center",
},
box: {
width: windowWidth > 500 ? "70%" : "90%",
height: windowHeight > 600 ? "50%" : "90%",
backgroundColor: "lightblue",
alignItems: "center",
justifyContent: "center",
borderRadius: 10,
},
text: {
fontSize: windowWidth > 500 ? 50 : 24,
color: "black",
fontWeight: "bold",
},
});- Dynamic UI can be complex to design and implement
- Dynamic UI can be difficult to maintain and update
- Dynamic UI can be difficult to test
import { useState, useEffect } from "react";
import { StyleSheet, Text, View, Dimensions } from "react-native";
export default function App() {
const [dimensions, setDimensions] = useState({
window: Dimensions.get("window"),
});
useEffect(() => {
const subscription = Dimensions.addEventListener(
"change",
({ window }) => {
setDimensions({ window });
}
);
return () => subscription?.remove();
});
const { window } = dimensions;
const windowWidth = window.width;
const windowHeight = window.height;
return (
<View style={styles.container}>
<View
style={[
styles.box,
{
width: windowWidth > 500 ? "70%" : "90%",
height: windowHeight > 600 ? "50%" : "90%",
},
]}
>
<Text style={{ fontSize: windowWidth > 500 ? 50 : 24 }}>
Welcome!
</Text>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "plum",
alignItems: "center",
justifyContent: "center",
},
box: {
backgroundColor: "lightblue",
alignItems: "center",
justifyContent: "center",
borderRadius: 10,
},
});UseWindowDimensions is a hook that returns the dimensions of the window in pixels.
It is better to use UseWindowDimensions than Dimensions.get("window")
import { Text, View } from "react-native";
import { useWindowDimensions } from "react-native";
export default function App() {
const { width, height } = useWindowDimensions();
return (
<View>
<Text>Width: {width}</Text>
<Text>Height: {height}</Text>
</View>
);
}safeAreaView is a component that ensures that the content of the app is not covered by the status bar, navigation bar, or other elements that may be present on the screen.
import { SafeAreaView, Text } from "react-native";
export default function App() {
return (
<SafeAreaView>
<Text>SafeAreaView</Text>
</SafeAreaView>
);
}- When developing a cross-platform app, maximizing code reuse is a priority
- There are situations where it becomes necessary to tailor your code to specific platforms
- React Native offers two approaches for organizing and separating platform-specific code:
- Platform module
- Platform-specific file extensions
Properties:
Platform.OS: A string that represents the current platform.Platform.Version: A number that represents the current platform version.Platform.select: A function that returns a value based on the current platform.Platform.OSX: A string that represents the macOS platform.Platform.IOS: A string that represents the iOS platform.Platform.ANDROID: A string that represents the Android platform.Platform.WEB: A string that represents the web platform.
*.ios.js: JavaScript file that is specific to the iOS platform*.android.js: JavaScript file that is specific to the Android platform*.web.js: JavaScript file that is specific to the web platform
import { Platform } from "react-native";
container: {
flex: 1,
backgroundColor: "plum",
paddingTop: Platform.OS === "android" ? 25 : 0,
}, text: {
...Platform.select({
ios: {
color: "purple",
fontSize: 24,
fontStyle: "italic",
},
android: {
color: "blue",
fontSize: 30,
},
}),
fontWeight: "bold",
textAlign: "center",
},The List component is a component that displays a list of items in a scrollable view.
FlatList is used to render large lists of data efficiently.
The source of items to render in the list.
Example:
const data = [
{ id: "1", title: "Item 1" },
{ id: "2", title: "Item 2" },
];Function that takes an item from data and renders it.
Example:
renderItem={({ item }) => <Text>{item.title}</Text>}Function to extract a unique key for each item.
Example:
keyExtractor={(item) => item.id}Component rendered at the top of the list.
Example:
ListHeaderComponent={<Text>Header</Text>}Component rendered at the bottom of the list.
Example:
ListFooterComponent={<Text>Footer</Text>}Component rendered between items.
Example:
ItemSeparatorComponent={() => <View style={{ height: 1, backgroundColor: 'gray' }} />}Renders list horizontally instead of vertically.
Example:
horizontal={true}How many columns to render.
Example:
numColumns={2}Called when the end of the list is reached.
Example:
onEndReached={() => console.log('Reached end')}Used for pull-to-refresh functionality.
Example:
const [refreshing, setRefreshing] = useState(false);
const onRefresh = () => {
setRefreshing(true);
// Fetch data here
setTimeout(() => setRefreshing(false), 2000);
};
<FlatList
data={data}
renderItem={renderItem}
keyExtractor={keyExtractor}
onRefresh={onRefresh}
refreshing={refreshing}
/>;Number of items to render initially.
Example:
initialNumToRender={10}Maximum number of items to render per batch.
Example:
maxToRenderPerBatch={5}Number of viewable screens worth of content to render ahead.
Example:
windowSize={10}Improves performance by specifying item height.
Example:
getItemLayout={(data, index) => (
{ length: 50, offset: 50 * index, index }
)}SectionList is used to render grouped lists of data, section by section.
An array of sections, each with a title and data.
Example:
const sections = [
{ title: "Fruits", data: ["Apple", "Banana"] },
{ title: "Vegetables", data: ["Carrot", "Tomato"] },
];Function that renders each item.
Example:
renderItem={({ item }) => <Text>{item}</Text>}Function that renders the header for each section.
Example:
renderSectionHeader={({ section: { title } }) => (
<Text style={{ fontWeight: 'bold' }}>{title}</Text>
)}Function to extract a unique key for each item.
Example:
keyExtractor={(item, index) => item + index}Component rendered at the top of the list.
Example:
ListHeaderComponent={<Text>Header</Text>}Component rendered at the bottom of the list.
Example:
ListFooterComponent={<Text>Footer</Text>}Component rendered between items.
Example:
ItemSeparatorComponent={() => <View style={{ height: 1, backgroundColor: 'gray' }} />}Component rendered between sections.
Example:
SectionSeparatorComponent={() => <View style={{ height: 10 }} />}Enables sticky section headers.
Example:
stickySectionHeadersEnabled={true}Called when the end of the list is reached.
Example:
onEndReached={() => console.log('End reached')}Used for pull-to-refresh functionality.
Example:
const [refreshing, setRefreshing] = useState(false);
const onRefresh = () => {
setRefreshing(true);
// Fetch data here
setTimeout(() => setRefreshing(false), 2000);
};
<SectionList
sections={sections}
renderItem={renderItem}
renderSectionHeader={renderSectionHeader}
keyExtractor={keyExtractor}
onRefresh={onRefresh}
refreshing={refreshing}
/>;Number of items to render initially.
Example:
initialNumToRender={5}Improves performance by specifying item height.
Example:
getItemLayout={(data, index) => (
{ length: 50, offset: 50 * index, index }
)}In web development, we have lots of HTML elements at our disposal to capture user input.
For example, input fields, text areas, dropdown menus, checkboxes, radio groups, and many more.
The core RN (React Native) library only provides TextInput and Switch.
These will be the sole focus of our learning in this section.
"What about the other components?"
Expo expands our toolkit, offering additional components like checkboxes and date pickers through the Expo SDK.
TextInput component is a fundamental building block for user input in React Native
It allows users to enter text and other data into your app
Props:
defaultValue: Sets the initial value of the input fieldvalue: Controls the current value of the input fieldonChangeText: Called when the text inside the input field changesonChange: Called when the value of the input field changesplaceholder: Text displayed when the input field is emptymultiline: Whether to allow multiple lines of textonSubmitEditing: Called when the user finishes editing the input fieldonEndEditing: Called when the user stops editing the input fieldonFocus: Called when the input field gains focusonBlur: Called when the input field loses focusplaceholderTextColor: Color of the placeholder textkeyboardType: Type of keyboard to usereturnKeyType: Type of return key to usesecureTextEntry: Whether to hide the text entered in the input fieldnumberOfLines: Maximum number of lines to displayeditable: Whether the input field is editableselectTextOnFocus: Whether to select the text when the input field gains focusclearTextOnFocus: Whether to clear the text when the input field gains focusmaxLength: Maximum length of the input field
<TextInput
value={name}
onChangeText={(text) => setName(text)}
style={styles.input}
placeholder="Enter your name"
autoCapitalize="none"
autoCorrect={false}
secureTextEntry
multiline
placeholderTextColor="gray"
numberOfLines={4}
editable={false}
selectTextOnFocus
clearTextOnFocus
maxLength={10}
returnKeyType="done"
returnKeyLabel="Done"
onSubmitEditing={() => console.log("Submitted")}
onEndEditing={() => console.log("Editing ended")}
onFocus={() => console.log("Focused")}
onBlur={() => console.log("Blurred")}
keyboardType="numeric"
keyboardAppearance="dark"
textContentType="oneTimeCode"
enablesReturnKeyAutomatically
blurOnSubmit
selectTextOnFocus
clearTextOnFocus
/>The Switch component serves as a valuable tool for integrating toggles into your app's user interface
It's particularly well-suited for scenarios where you require users to make binary choices, such as enabling or disabling specific app features.
Props:
value: The current state of the switch (true for on, false for off)onValueChange: Called when the switch's state changesdisabled: Whether the switch is disableddisabledThumbColor: Color of the thumb when the switch is disableddisabledTrackColor: Color of the track when the switch is disabledtrackColor: Color of the trackthumbColor: Color of the thumbtrackBorderColor: Color of the track borderthumbBorderColor: Color of the thumb bordertrackBorderWidth: Width of the track borderthumbBorderWidth: Width of the thumb borderthumbSize: Size of the thumbtrackSize: Size of the trackios_backgroundColor: Background color for iOSios_tintColor: Tint color for iOSios_thumbTintColor: Thumb tint color for iOS
<Switch
value={isEnabled}
onValueChange={toggleSwitch}
disabled={false}
disabledThumbColor="gray"
disabledTrackColor="gray"
trackColor={{ false: "#767577", true: "#81b0ff" }}
thumbColor={isEnabled ? "#f5dd4b" : "#f4f3f4"}
trackBorderColor="gray"
thumbBorderColor="gray"
trackBorderWidth={2}
thumbBorderWidth={2}
thumbSize={20}
trackSize={40}
ios_backgroundColor="#3e3e3e"
ios_tintColor="#3e3e3e"
ios_thumbTintColor="#3e3e3e"
/>Managing Form State: Controlling the dynamic data within our forms
Handling Form Validation: Ensuring the integrity and validity of the user's input
Displaying Validation Messages: Communicating the results of validation to the user
Submitting Form Data: Sending the collected information where it needs to go
email: Email addresspassword: Password
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const handleLogin = () => {
// Perform login logic here
console.log("Email:", email);
console.log("Password:", password);
};behavior: Keyboard behavior (e.g., "position", "height", "padding")keyboardVerticalOffset: Vertical offset for the keyboard
return (
<KeyboardAvoidingView
behavior="padding"
keyboardVerticalOffset={Platform.OS === "ios" ? 100 : 0}
style={styles.container}
>
<View style={styles.form}>
<Image
source={require("./assets/adaptive-icon.png")}
style={styles.logo}
/>
<Text style={styles.label}>Username</Text>
<TextInput
style={styles.input}
placeholder="Enter your username"
value={username}
onChangeText={setUsername}
/>
{errors.username ? (
<Text style={styles.errorText}> {errors.username}</Text>
) : null}
<Text style={styles.label}>Password</Text>
<TextInput
style={styles.input}
placeholder="Enter your password"
secureTextEntry
autoCapitalize="none"
autoCorrect={false}
value={password}
onChangeText={setPassword}
/>
{errors.password ? (
<Text style={styles.errorText}> {errors.password}</Text>
) : null}
<Button title="Login" onPress={handleSubmit} />
</View>
</KeyboardAvoidingView>
);Form validation is a crucial step in ensuring the accuracy and integrity of user input. It involves evaluating the data entered by the user and ensuring that it meets the specified criteria.
export default function App() {
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const [errors, setErrors] = useState({});
const validateForm = () => {
let errors = {};
if (!username) errors.username = "Username is required";
if (!password) errors.password = "Password is required";
setErrors(errors);
return Object.keys(errors).length === 0;
};
return (
<Text style={styles.label}>Username</Text>
<TextInput/>
{
errors.username ? (
<Text style={styles.errorText}> {errors.username}</Text>
) : null
}
<Text style={styles.label}>Password</Text>
<TextInput/>
{
errors.password ? (
<Text style={styles.errorText}> {errors.password}</Text>
) : null
}
<Button title="Login" onPress={validateForm} />
)Form submission is the process of sending the collected data to a server or other destination for processing or storage.
const handleSubmit = () => {
if (validateForm()) {
console.log("Submitted", username, password);
setTimeout(() => {
setUsername("");
setPassword("");
setErrors({});
}, 0);
}
};
<Button title="Login" onPress={handleSubmit} />;- Fetching and submitting data to an API
- Loading states
- Error handling
- FlatList component to display our data
GET requests are used to retrieve data from a server.
Parameters:
url: The URL of the API endpointheaders: Optional headers to include in the requestparams: Optional query parameters to include in the request
import { useEffect, useState } from "react";
export default function App() {
const [postList, setPostList] = useState([]);
const fetchData = async (limit = 10) => {
const Response = await fetch(
`https://jsonplaceholder.typicode.com/posts?_limit=${limit}`
);
const data = await Response.json();
setPostList(data);
};
useEffect(() => {
fetchData();
}, []);
return (
<SafeAreaView style={styles.container}>
<View style={styles.listContainer}>
<FlatList
data={postList}
renderItem={({ item }) => {
return (
<View style={styles.card}>
<Text style={styles.titleText}>{item.title}</Text>
<Text style={styles.bodyText}>{item.body}</Text>
</View>
);
}}
ItemSeparatorComponent={() => <View style={{ height: 16 }} />}
ListEmptyComponent={<Text>No Post Found</Text>}
ListHeaderComponent={
<Text style={styles.headerText}>Post List</Text>
}
ListFooterComponent={
<Text style={styles.footerText}>End of List</Text>
}
/>
</View>
</SafeAreaView>
);
}The loading state is used to indicate that data is being fetched from the server.
Parameters:
isLoading: A boolean value indicating whether data is being fetched from the server
const [IsLoading, setIsLoading] = useState(true);
const fetchData = async (limit = 10) => {
const Response = await fetch(
`https://jsonplaceholder.typicode.com/posts?_limit=${limit}`
);
const data = await Response.json();
setPostList(data);
setIsLoading(false);
};
if (IsLoading) {
return (
<SafeAreaView style={styles.loadingContainer}>
<ActivityIndicator size="large" color={"blue"} />
<Text>Loading...</Text>
</SafeAreaView>
);
}Post requests are used to submit data to a server.
Parameters:
url: The URL of the API endpointheaders: Optional headers to include in the requestbody: Optional body to include in the request
export default function App() {
const [postList, setPostList] = useState([]);
const [IsLoading, setIsLoading] = useState(true);
const [refreshing, setRefreshing] = useState(false);
const [postBody, setPostBody] = useState("");
const [postTitle, setPostTitle] = useState("");
const [isPosting, setIsPosting] = useState(false);
const fetchData = async (limit = 10) => {
const Response = await fetch(
`https://jsonplaceholder.typicode.com/posts?_limit=${limit}`
);
const data = await Response.json();
setPostList(data);
setIsLoading(false);
};
const addPost = async () => {
setIsPosting(true);
const Response = await fetch(
"https://jsonplaceholder.typicode.com/posts",
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
title: postTitle,
body: postBody,
}),
}
);
const newPost = await Response.json();
setPostList([newPost, ...postList]);
setPostTitle("");
setPostBody("");
setIsPosting(false);
};
return (
<SafeAreaView style={styles.container}>
<>
<View style={styles.inputContainer}>
<TextInput
style={styles.input}
placeholder="Post Title"
value={postTitle}
onChangeText={setPostTitle}
/>
<TextInput
style={styles.input}
placeholder="Post Body"
value={postBody}
onChangeText={setPostBody}
/>
<Button
title={isPosting ? "Posting..." : "Add Post"}
onPress={addPost}
disabled={isPosting}
/>
</View>
</>
</SafeAreaView>
);
}Error handling is used to handle errors that occur during the execution of a program.
import {
SafeAreaView,
StatusBar,
StyleSheet,
View,
FlatList,
Text,
ActivityIndicator,
TextInput,
Button,
} from "react-native";
import { useEffect, useState } from "react";
export default function App() {
const [postList, setPostList] = useState([]);
const [IsLoading, setIsLoading] = useState(true);
const [refreshing, setRefreshing] = useState(false);
const [postBody, setPostBody] = useState("");
const [postTitle, setPostTitle] = useState("");
const [isPosting, setIsPosting] = useState(false);
const [error, setError] = useState("");
const fetchData = async (limit = 10) => {
try {
const Response = await fetch(
`https://jsonplaceholder.typicode.com/posts?_limit=${limit}`
);
const data = await Response.json();
setPostList(data);
setIsLoading(false);
setError("");
} catch (error) {
console.error("Error fetching data:", error);
setIsLoading(false);
setError("Failed to fetch post list");
}
};
const addPost = async () => {
setIsPosting(true);
try {
const Response = await fetch(
"https://jsonplaceholder.typicode.com/posts",
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
title: postTitle,
body: postBody,
}),
}
);
const newPost = await Response.json();
setPostList([newPost, ...postList]);
setPostTitle("");
setPostBody("");
setIsPosting(false);
setError("");
} catch (error) {
console.error("Error posting data:", error);
setIsPosting(false);
setError("Failed to add new post");
}
};
if (IsLoading) {
return (
<SafeAreaView style={styles.loadingContainer}>
<ActivityIndicator size="large" color={"blue"} />
<Text>Loading...</Text>
</SafeAreaView>
);
}
return (
<SafeAreaView style={styles.container}>
{error ? (
<View style={styles.errorContainer}>
<Text style={styles.errorText}>{error}</Text>
</View>
) : (
<>
<View style={styles.inputContainer}>
<TextInput
style={styles.input}
placeholder="Post Title"
value={postTitle}
onChangeText={setPostTitle}
/>
<TextInput
style={styles.input}
placeholder="Post Body"
value={postBody}
onChangeText={setPostBody}
/>
<Button
title={isPosting ? "Posting..." : "Add Post"}
onPress={addPost}
disabled={isPosting}
/>
</View>
<View style={styles.listContainer}>
<FlatList
data={postList}
renderItem={({ item }) => {
return (
<View style={styles.card}>
<Text style={styles.titleText}>{item.title}</Text>
<Text style={styles.bodyText}>{item.body}</Text>
</View>
);
}}
ItemSeparatorComponent={() => (
<View style={{ height: 16 }} />
)}
ListEmptyComponent={<Text>No Post Found</Text>}
ListHeaderComponent={
<Text style={styles.headerText}>Post List</Text>
}
ListFooterComponent={
<Text style={styles.footerText}>End of List</Text>
}
refreshing={refreshing}
onRefresh={handleRefresh}
/>
</View>
</>
)}
</SafeAreaView>
);
}- The mechanism that allows users to move across different screens, access features, and generally use your app effectively
- A go-to solution for handling navigation is the React Navigation library
- Expo has its own built-in routing feature exclusive to Expo projects
- React Navigation works both with and without Expo in React Native apps
- Focus on React Navigation in this section
- Provides a variety of navigators like Stack, Drawer, and Tab navigators
- Stack Navigator provides a way for your app to transition between screens where each new screen is placed on top of a stack
- Drawer Navigator renders a navigation drawer on the side of the screen which can be opened and closed via gestures
- Tab Navigator at the bottom of your screen lets you easily switch between different routes
"react-navigation": "^4.4.4",
"react-navigation-stack": "^2.10.4",
"react-navigation-drawer": "^2.7.1",
"react-navigation-tabs": "^2.11.2",npm install @react-navigation/native
npx expo install react-native-screens react-native-safe-area-context
npm install @react-navigation/native-stack
import { NavigationContainer } from "@react-navigation/native";
export default function App() {
return <NavigationContainer></NavigationContainer>;
}Each new screen is stacked on top of the previous one like a deck of cards.
- When navigating to a new screen, a new card is placed on top of the stack.
- When navigating back, the top card is removed, revealing the previous screen.
This approach allows users to drill down into detailed views and then retrace their steps when done.
It is particularly useful in scenarios where a linear flow of screens is required.
Example Navigation Flow:
List View → Details View → More Details ViewTwo navigators: Stack Navigator and Native Stack Navigator
The Stack Navigator is a JavaScript-based navigator which offers a high degree of customization,
making it a great choice for apps that require a unique navigation experience.
However, this comes at the cost of performance — especially when compared to its counterpart,
the Native Stack Navigator.
The Native Stack Navigator leverages the native navigation constructs of iOS and Android,
providing better performance and a more native feel to the transitions and gestures.
The caveat here is, it might not offer the same level of customization as the Stack Navigator.
import { NavigationContainer } from "@react-navigation/native";
import { createNativeStackNavigator } from "@react-navigation/native-stack";
import Home_Screen from "./screens/Home_Screen";
import About_Screen from "./screens/About_Screen";
const Stack = createNativeStackNavigator();
export default function App() {
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="About">
<Stack.Screen name="Home" component={Home_Screen} />
<Stack.Screen name="About" component={About_Screen} />
</Stack.Navigator>
</NavigationContainer>
);
}We can navigate to a new screen by calling the navigate method on the navigator instance.
Methods:
navigate(name, params): Navigates to the screen with the given name and optional parameters.goBack(): Navigates back to the previous screen.replace(name, params): Replaces the current screen with the screen with the given name and optional parameters.reset(state): Resets the navigation state to the given state.pop(): Navigates back to the previous screen.popToTop(): Navigates back to the root screen.
App.js
import { NavigationContainer } from "@react-navigation/native";
import { createNativeStackNavigator } from "@react-navigation/native-stack";
import Home_Screen from "./screens/Home_Screen";
import About_Screen from "./screens/About_Screen";
const Stack = createNativeStackNavigator();
export default function App() {
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="Home">
<Stack.Screen name="Home" component={Home_Screen} />
<Stack.Screen name="About" component={About_Screen} />
</Stack.Navigator>
</NavigationContainer>
);
}Home_Screen.js
Using the navigation method to navigate to a new screen:
import { View, Text, StyleSheet, Button } from "react-native";
export default function Home_Screen({ navigation }) {
return (
<View style={styles.container}>
<Text style={styles.text}>Home Screen</Text>
<Button
title="Go to About"
onPress={() => navigation.navigate("About")}
/>
</View>
);
}About_Screen.js
Using the useNavigation hook to navigate to a new screen:
import { View, Text, StyleSheet, Button } from "react-native";
import { useNavigation } from "@react-navigation/native";
export default function About_Screen() {
const navigation = useNavigation();
return (
<View style={styles.container}>
<Text style={styles.text}>About Screen</Text>
<Button
title="Go to Home"
onPress={() => navigation.navigate("Home")}
/>
</View>
);
}We can pass data between screens using the params property of the navigate method.
Home_Screen.js
import { View, Text, StyleSheet, Button } from "react-native";
export default function Home_Screen({ navigation, route }) {
return (
<View style={styles.container}>
<Text style={styles.text}>Home Screen</Text>
<Button
title="Go to About"
onPress={() => navigation.navigate("About", { name: "Luffy" })}
/>
<Text style={styles.text}>Result: {route.params?.result}</Text>
</View>
);
}About_Screen.js
import { View, Text, StyleSheet, Button } from "react-native";
export default function About_Screen({ navigation, route }) {
const { name } = route.params;
return (
<View style={styles.container}>
<Text style={styles.text}>About {name}</Text>
<Button
title="Update the name"
onPress={() => navigation.setParams({ name: "Sanji" })}
/>
<Button
title="Go back with data"
onPress={() => {
navigation.navigate("Home", { result: "Data from About" });
}}
/>
</View>
);
}We can set navigation options for each screen using the options property of the Stack.Screen component.
Methods:
title: Sets the title of the screen.headerStyle: Sets the style of the header.headerTitle: Sets the title of the header.headerTitleAlign: Sets the alignment of the title in the header.headerTitleStyle: Sets the style of the title in the header.headerBackTitle: Sets the title of the back button in the header.headerBackTitleVisible: Sets whether the back button title is visible in the header.headerBackTitleStyle: Sets the style of the back button title in the header.headerBackVisible: Sets whether the back button is visible in the header.headerTintColor: Sets the color of the header.headerRight: Sets the right component of the header.headerLeft: Sets the left component of the header.headerRightContainerStyle: Sets the style of the right container in the header.headerLeftContainerStyle: Sets the style of the left container in the header.headerRightStyle: Sets the style of the right component in the header.headerLeftStyle: Sets the style of the left component in the header.contentStyle: Sets the style of the content in the screen.
App.js
import { NavigationContainer } from "@react-navigation/native";
import { createNativeStackNavigator } from "@react-navigation/native-stack";
import Home_Screen from "./screens/Home_Screen";
import About_Screen from "./screens/About_Screen";
import { Pressable, Text } from "react-native";
const Stack = createNativeStackNavigator();
export default function App() {
return (
<NavigationContainer>
<Stack.Navigator
initialRouteName="Home"
screenOptions={{
headerStyle: { backgroundColor: "#6a51ae" },
headerTintColor: "#fff",
headerTitleStyle: { fontWeight: "bold" },
headerTitleAlign: "center",
headerRight: () => (
<Pressable onPress={() => alert("Menu button pressed!")}>
<Text style={{ color: "white", fontSize: 20 }}>Menu</Text>
</Pressable>
),
contentStyle: { backgroundColor: "#e8e4f3" },
}}
>
<Stack.Screen
name="Home"
component={Home_Screen}
options={{
title: "Welcome Home!",
}}
/>
<Stack.Screen name="About" component={About_Screen} />
</Stack.Navigator>
</NavigationContainer>
);
}This applies the navigation options to both the Home and About screens.
If we want to apply the navigation options to only the Home screen, we can do the following:
<Stack.Screen
name="Home"
component={Home_Screen}
options={{
title: "Welcome Home!",
headerStyle: { backgroundColor: "#6a51ae" },
headerTintColor: "#fff",
headerTitleStyle: { fontWeight: "bold" },
headerTitleAlign: "center",
}}We can set navigation options for each screen using the options property of the Stack.Screen component.
We can also set navigation options for the entire navigator using the screenOptions property of the Stack.Navigator component.
App.js
<Stack.Screen
name="Home"
component={Home_Screen}
options={{
title: "Welcome Home!",
}}
/>
<Stack.Screen
name="About"
component={About_Screen}
initialParams={{
name: "Guest",
}}
/* options={({ route }) => ({
title: route.params.name,
})} */
/>About_Screen.js
export default function About_Screen({ navigation, route }) {
const { name } = route.params;
useLayoutEffect(() => {
navigation.setOptions({
title: name,
});
}, [navigation, name]);
return (
<View style={styles.container}>
<Text style={styles.text}>About {name}</Text>
<Button
title="Update the name"
onPress={() => navigation.setParams({ name: "Sanji" })}
/>
<Button
title="Go back with data"
onPress={() => {
navigation.navigate("Home", { result: "Data from About" });
}}
/>
</View>
);
}Home_Screen.js
export default function Home_Screen({ navigation, route }) {
return (
<View style={styles.container}>
<Text style={styles.text}>Home Screen</Text>
<Text style={styles.text}>Result: {route.params?.result}</Text>
<Button
title="Go to About"
onPress={() =>
navigation.navigate("About", {
name: "Luffy",
})
}
/>
</View>
);
}Drawer Navigator introduces a hidden menu, sliding from either side of the screen.
It is particularly beneficial in apps with multiple main sections that require a neat and organized navigation structure.
Dependencies:
npm install @react-navigation/drawer
npx expo install react-native-gesture-handler react-native-reanimatedimport "react-native-gesture-handler";
import { NavigationContainer } from "@react-navigation/native";
import { createNativeStackNavigator } from "@react-navigation/drawer";
const Drawer = createNativeStackNavigator();https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/getting-started/
babel.config.js
Add the plugin to your babel.config.js file:
plugins: [
...
'react-native-reanimated/plugin',
],
Like This:
module.exports = function (api) {
api.cache(true);
return {
presets: ["babel-preset-expo"],
plugins: ["react-native-reanimated/plugin"],
};
};Add the following command to your package.json file:
npx expo start -cLike This:
"scripts": {
"start": "expo start -c", // <-- Here
"android": "expo start --android",
"ios": "expo start --ios",
"web": "expo start --web"
},App.js
import "react-native-gesture-handler";
import { NavigationContainer } from "@react-navigation/native";
import { createDrawerNavigator } from "@react-navigation/drawer";
import DashboardScreen from "./screens/DashboardScreen";
import SettingsScreen from "./screens/SettingsScreen";
const Drawer = createDrawerNavigator();
export default function App() {
return (
<NavigationContainer>
<Drawer.Navigator>
<Drawer.Screen name="Dashboard" component={DashboardScreen} />
<Drawer.Screen name="Settings" component={SettingsScreen} />
</Drawer.Navigator>
</NavigationContainer>
);
}DashboardScreen.js
import { View, Text, StyleSheet } from "react-native";
const DashboardScreen = () => {
return (
<View style={styles.container}>
<Text style={styles.text}>DashboardScreen</Text>
</View>
);
};
export default DashboardScreen;SettingsScreen.js
import { View, Text, StyleSheet } from "react-native";
const SettingsScreen = () => {
return (
<View style={styles.container}>
<Text style={styles.text}>SettingsScreen</Text>
</View>
);
};
export default SettingsScreen;Remember to destructure the
navigationobject:
const DashboardScreen = ({ navigation }) => {
return (
...
)
}To Open the Drawer
<Button title="Toggle Drawer" onPress={() => navigation.toggleDrawer()} />To Navigate to a Screen
<Button title="Go to Settings" onPress={() => navigation.jumpTo("Settings")} />To Navigate Back
<Button title="Go Back" onPress={() => navigation.goBack()} />To Navigate Back to the First Screen
<Button title="Go Back to First Screen" onPress={() => navigation.popToTop()} />Drawer Navigator allows you to set screen-specific options using the options prop in each Drawer.Screen.
This are the 5 most used options in drawer navigation:
<Drawer.Screen
name="Dashboard"
component={DashboardScreen}
options={{
title: "My Dashboard",
drawerLabel: "Dashboard Label",
drawerActiveTintColor: "#333",
drawerActiveBackgroundColor: "lightblue",
drawerContentStyle: {
backgroundColor: "#c6cbef",
},
}}
/>You can find more at: https://reactnavigation.org/docs/drawer-navigator
| Option | Description |
|---|---|
title |
Title for the screen (also used as drawer label if drawerLabel is absent). |
headerShown |
Show or hide the header. |
headerStyle |
Style for the header container. |
headerTitleStyle |
Style for the header title text. |
headerTitleAlign |
Align header title: "left" or "center". |
headerTitle |
Custom title or component. |
headerTintColor |
Color for back button and title text. |
headerTransparent |
Make the header background transparent. |
headerBackVisible |
Show/hide the back button. |
headerBackTitleVisible |
Show/hide the back title (iOS only). |
headerBackTitle |
Set custom text for the back button (iOS only). |
headerBackTitleStyle |
Style for back button title (iOS only). |
headerBackAccessibilityLabel |
Accessibility label for back button. |
headerBackAccessibilityHint |
Accessibility hint for back button. |
headerLeft |
Component/function for left side of header. |
headerRight |
Component/function for right side of header. |
headerLeftContainerStyle |
Style for the left container. |
headerRightContainerStyle |
Style for the right container. |
headerBackground |
Custom background component. |
headerShadowVisible |
Show/hide header shadow (iOS and Android). |
headerBlurEffect |
Apply blur effect (iOS only). |
| Option | Description |
|---|---|
drawerLabel |
Label to show in the drawer. Defaults to title or name prop. |
drawerIcon |
Function that returns an icon to display in the drawer. |
drawerActiveTintColor |
Color for the label and icon of the active item. |
drawerInactiveTintColor |
Color for the label and icon of the inactive items. |
drawerActiveBackgroundColor |
Background color for the active drawer item. |
drawerInactiveBackgroundColor |
Background color for inactive drawer items. |
drawerLabelStyle |
Style object for the drawer label. |
drawerItemStyle |
Style object for the individual drawer item container. |
drawerType (in Drawer.Navigator) |
Animation type of the drawer: front, back, slide, or permanent. |
Tab Navigator introduces a row of tabs (usually at the bottom) to switch between top-level screens.
It’s ideal for apps with a small number of main sections that users switch between frequently.
Dependencies:
npm install @react-navigation/bottom-tabs
npx expo install react-native-gesture-handler react-native-reanimatedimport { NavigationContainer } from "@react-navigation/native";
import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
const Tab = createBottomTabNavigator();https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/getting-started/
If you’re using Reanimated-based animations, add to babel.config.js:
babel.config.js
plugins: [
...
'react-native-reanimated/plugin',
],
Like This:
module.exports = function (api) {
api.cache(true);
return {
presets: ["babel-preset-expo"],
plugins: ["react-native-reanimated/plugin"],
};
};Ensure to clear Metro’s cache for Reanimated changes to take effect.
Add the following command to your package.json file:
npx expo start -cLike This:
"scripts": {
"start": "expo start -c", // <-- Here
"android": "expo start --android",
"ios": "expo start --ios",
"web": "expo start --web"
},Basic Structure:
App.js
import { NavigationContainer } from "@react-navigation/native";
import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
import ProfileScreen from "./screens/ProfileScreen";
import CourseListScreen from "./screens/CourseListScreen";
import SettingsScreen from "./screens/SettingsScreen";
const Tab = createBottomTabNavigator();
export default function App() {
return (
<NavigationContainer>
<Tab.Navigator>
<Tab.Screen name="Profile" component={ProfileScreen} />
<Tab.Screen name="Course List" component={CourseListScreen} />
<Tab.Screen name="Settings" component={SettingsScreen} />
</Tab.Navigator>
</NavigationContainer>
);
}ProfileScreen.js | CourseListScreen.js | SettingsScreen.js
Note: All screens have the same structure.
import { View, Text, StyleSheet } from "react-native";
const ProfileScreen = () => {
return (
<View style={styles.container}>
<Text style={styles.text}>Profile Screen</Text>
</View>
);
};
export default ProfileScreen;Tab Navigator supports options for each tab. These options are passed to the Tab.Screen component.
Basic Structure:
import { NavigationContainer } from "@react-navigation/native";
import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
import Ionicons from "@expo/vector-icons/Ionicons";
<NavigationContainer>
<Tab.Navigator
screenOptions={{
tabBarLabelPosition: "below-icon",
tabBarShowLabel: true,
tabBarActiveTintColor: "Purple",
}}
>
<Tab.Screen
name="Profile"
component={ProfileScreen}
options={{
tabBarLabel: "My Profile",
tabBarIcon: ({ color }) => (
<Ionicons name="person" color={color} size={20} />
),
tabBarBadge: 3,
}}
/>
</Tab.Navigator>
</NavigationContainer>;| Option | Description |
|---|---|
tabBarLabel |
Label for the tab. Defaults to title or name. |
tabBarIcon |
Function that returns an icon to display in the tab. |
tabBarBadge |
Show a badge on the tab (e.g., number or string). |
tabBarBadgeStyle |
Style object for the badge. |
tabBarAccessibilityLabel |
Accessibility label for the tab. |
tabBarAccessibilityHint |
Accessibility hint for the tab. |
tabBarActiveTintColor |
Color for the active tab icon and label. |
tabBarInactiveTintColor |
Color for the inactive tab icon and label. |
tabBarActiveBackgroundColor |
Background color for the active tab. |
tabBarInactiveBackgroundColor |
Background color for inactive tabs. |
tabBarLabelStyle |
Style object for the tab label text. |
tabBarIconStyle |
Style object for the tab icon. |
tabBarItemStyle |
Style object for the individual tab item. |
tabBarStyle |
Style for the overall tab bar container. |
tabBarShowLabel |
Whether to show the label text below the icon. |
tabBarHideOnKeyboard |
Whether to hide the tab bar when the keyboard is visible (Android only). |
You can find more options for Tab.Screen in the official documentation.
Nesting navigators allows us to combine the powers of different types of navigators, creating a seamless and organized user experience.
It’s like having a main road with smaller branching lanes, each having its own set of rules yet interconnected.
This is a simple example of a nested navigator. We take the Stack Navigator and nest it inside a Tab Navigator.
import { NavigationContainer } from "@react-navigation/native";
import { createNativeStackNavigator } from "@react-navigation/native-stack";
import Home_Screen from "./screens/Home_Screen";
import About_Screen from "./screens/About_Screen";
import { Pressable, Text } from "react-native";
const Stack = createNativeStackNavigator();
export const AboutStack = () => {
return (
<Stack.Navigator
initialRouteName="Home"
screenOptions={{
headerStyle: { backgroundColor: "#6a51ae" },
headerTintColor: "#fff",
headerTitleStyle: { fontWeight: "bold" },
headerTitleAlign: "center",
headerRight: () => (
<Pressable onPress={() => alert("Menu button pressed!")}>
<Text style={{ color: "white", fontSize: 20 }}>Menu</Text>
</Pressable>
),
contentStyle: { backgroundColor: "#e8e4f3" },
}}
>
<Stack.Screen
name="Home"
component={Home_Screen}
options={{
title: "Welcome Home!",
}}
/>
<Stack.Screen
name="About"
component={About_Screen}
initialParams={{
name: "Guest",
}}
/* options={({ route }) => ({
title: route.params.name,
})} */
/>
</Stack.Navigator>
);
};
export default function App() {
return (
<NavigationContainer>
<AboutStack />
</NavigationContainer>
);
}Like this:
import { AboutStack } from "./stackApp";
<Tab.Screen
name="About Stack"
component={AboutStack}
options={{
tabBarLabel: "Home Stack",
headerShown: false,
tabBarIcon: ({ color }) => (
<Ionicons name="home" color={color} size={20} />
),
}}
/>;

