Create a GPA Calculator using React Native
A GPA calculator proves to be a useful tool for students who want to monitor their academic progress. In this article, we will build a GPA calculator using React Native, a popular framework for building mobile applications.

To give you a better idea of what we’re going to create, let’s watch a demo video.
Demo Video
Playground
Note: This Section is to interact with the app which you are going to build.
Prerequisites
- Introduction to React Native
- React Native Components
- React Hooks
- Node.js and NPM
Step-by-Step Implementation
Step 1: Create a React Native Project
Now, create a project with the following command.
npx create-expo-app app-name --template
Note: Replace the app-name with your app name for example : react-native-demo-app
Next, you might be asked to choose a template. Select one based on your preference as shown in the image below. I am selecting the blank template because it will generate a minimal app that is as clean as an empty canvas in JavaScript.

It completes the project creation and displays a message: "Your Project is ready!" as shown in the image below.

Now go into your project folder, i.e., react-native-demo
cd app-name
Project Structure:

Step 2: Run Application
Start the server by using the following command.
npx expo start
Then, the application will display a QR code.
- For the Android users,
- For the Android Emulator, press " a" as mentioned in the image below.
- For the Physical Device, download the " Expo Go " app from the Play Store. Open the app, and you will see a button labeled " Scan QR Code. " Click that button and scan the QR code; it will automatically build the Android app on your device.
- For iOS users, simply scan the QR code using the Camera app.
- If you're using a web browser, it will provide a local host link that you can use as mentioned in the image below.

Step 3: Add dependencies
Add the below dependencies in package.json.
package.json
{
"dependencies": {
"react-native-paper": "^5.14.0",
"react-native-picker-select": "^9.3.1",
}
}
Now, run the following command in the terminal to install the above dependencies.
npm install
Step 4: Start Coding
- Approach:
The GPA Calculator app, developed using React Native, offers a seamless experience for users to input their course information, including course name, credit hours, and grade. With its user-friendly interface featuring text input fields, a grade picker, and interactive buttons for adding and deleting courses, the app dynamically calculates the user's GPA based on the entered data. Powered by React Native's state management capabilities, it efficiently captures user input, performs calculations, and showcases the comprehensive course list alongside the calculated GPA.
Example: The given illustrations are a mobile app called GPA Calculator, developed using React Native. This app comprises three main components: CourseForm, CourseList, and GPACalculator. The CourseForm component is responsible for managing the input of course data and ensures validation before adding new courses. On the other hand, the CourseList component displays the list of courses with options to delete entries. Lastly, the GPACalculator component orchestrates the entire application by calculating the GPA based on entered courses and presenting it alongside the course list.
Let's dive into the code in detail.
- Import libraries:
Import required libraries at the top of the file.
// Import useState hook from React
import { useState } from "react";
// Import core React Native UI components
import {
View, // Container component for layout
Text, // Component for displaying text
TextInput, // Input field for user text entry
TouchableOpacity, // Pressable component for buttons
ScrollView, // Scrollable container for lists
} from "react-native";
// Import picker select component for dropdowns
import RNPickerSelect from "react-native-picker-select";
// Import styles from external stylesheet
import { styles } from "./styles";
- StyleSheet:
Create a StyleSheet to style components like container, title, section, etc.
// Import the StyleSheet module from react-native for creating styles
import { StyleSheet } from "react-native";
// Create a StyleSheet object containing all styles for the app
const styles = StyleSheet.create({
// Container style for the main layout
container: {
flex: 1, // Take up the full available space
padding: 16, // Add padding inside the container
backgroundColor: "#f0f0f0", // Set background color
},
// Style for the main title text
title: {
fontSize: 25, // Set font size
fontWeight: "bold", // Make text bold
marginBottom: 10, // Space below the title
textAlign: "center", // Center the text
color: "#333", // Set text color
margin: 10, // Margin on all sides
paddingTop: 30, // Padding at the top
},
// Style for each section/card
section: {
marginBottom: 16, // Space below the section
backgroundColor: "#fff", // Section background color
padding: 20, // Padding inside the section
borderRadius: 5, // Rounded corners
borderWidth: 1, // Border width
borderColor: "#ced4da", // Border color
shadowColor: "grey", // Shadow color (iOS)
shadowOffset: {
width: 0, // Shadow offset horizontally
height: 6, // Shadow offset vertically
},
shadowOpacity: 1, // Shadow opacity (iOS)
shadowRadius: 15, // Shadow blur radius (iOS)
elevation: 5, // Shadow elevation (Android)
},
// Style for labels
label: {
fontSize: 16, // Font size
marginBottom: 8, // Space below the label
color: "#333", // Text color
},
// Style for input fields
input: {
borderColor: "#ced4da", // Border color
borderWidth: 1, // Border width
borderRadius: 4, // Rounded corners
padding: 8, // Padding inside input
marginBottom: 8, // Space below input
fontSize: 16, // Font size
color: "#333", // Text color
},
// Style for headers
header: {
fontSize: 18, // Font size
fontWeight: "bold", // Bold text
marginBottom: 10, // Space below header
textAlign: "center", // Center the text
color: "#333", // Text color
},
// Style for the list container
list: {
maxHeight: 200, // Maximum height for the list
},
// Style for the list header row
listHeader: {
flexDirection: "row", // Arrange children in a row
justifyContent: "space-between", // Space between items
borderBottomWidth: 1, // Bottom border width
borderColor: "#ccc", // Border color
marginBottom: 10, // Space below header
paddingBottom: 10, // Padding below header
},
// Style for each list item row
listItem: {
flexDirection: "row", // Arrange children in a row
justifyContent: "space-between", // Space between items
borderBottomWidth: 1, // Bottom border width
borderColor: "#ccc", // Border color
marginBottom: 8, // Space below item
paddingBottom: 8, // Padding below item
},
// Style for GPA display
gpa: {
fontSize: 20, // Font size
fontWeight: "bold", // Bold text
marginTop: 10, // Space above GPA
textAlign: "center", // Center the text
color: "#333", // Text color
},
// Style for the add button
addButton: {
padding: 9.5, // Padding inside button
backgroundColor: "#fff", // Button background color
color: "#1d9bf0", // Text color
borderWidth: 1, // Border width
borderColor: "#1d9bf0", // Border color
borderRadius: 4, // Rounded corners
fontWeight: "bold", // Bold text
transition: "0.5s all", // Transition effect (not supported in React Native)
textAlign: "center", // Center the text
alignContent: "center", // Align content vertically
alignItems: "center", // Align items horizontally
justifyContent: "center", // Center content
},
// Style for add button when hovered (not supported in React Native)
addButtonHover: {
color: "white", // Text color on hover
backgroundColor: "#1d9bf0", // Background color on hover
borderColor: "#1d9bf0", // Border color on hover
borderWidth: 1, // Border width on hover
},
// Style for delete button
deletebtn: {
padding: 9.5, // Padding inside button
color: "#1d9bf0", // Text color
fontWeight: "bold", // Bold text
textAlign: "center", // Center the text
backgroundColor: "red", // Button background color
borderRadius: 15, // Rounded corners
},
// Style for delete button text
deletebtntext: {
color: "white", // Text color
},
// Style for picker component
picker: {
fontSize: 16, // Font size
paddingVertical: 12, // Vertical padding
paddingHorizontal: 10, // Horizontal padding
borderWidth: 1, // Border width
borderColor: "gray", // Border color
borderRadius: 4, // Rounded corners
color: "black", // Text color
paddingRight: 30, // Padding on the right
},
});
// Export the styles object for use in other files
export { styles };
- Grade Points:
Add grade points globally.
// Define grade points mapping for each grade
const gradePoints = {
"A+": 4.0,
A: 4.0,
"A-": 3.7,
"B+": 3.3,
B: 3.0,
"B-": 2.7,
"C+": 2.3,
C: 2.0,
"C-": 1.7,
"D+": 1.3,
D: 1.0,
"D-": 0.7,
};
- Title Text:
This title explains what the app does. We use the text "GPA Calculator" to show that the app is used to calculate GPA.
{/*Title Text */}
<Text style={styles.title}>GPA Calculator</Text>
- Course Form:
This form is for adding a course. It includes two TextInput components where you can enter the course name and the number of credits you earned. There is also a grade selection list using RNPickerSelect, which lets you choose your grade. Finally, there's an "Add" button to add the course to your list.
// Form component to add a new course
const CourseForm = ({ onAddCourse }) => {
// State to hold course input values
const [courseInfo, setCourseInfo] = useState({
courseName: "",
creditHours: "0",
grade: "A+",
});
// Handle input changes for form fields
const handleInputChange = (name, value) => {
setCourseInfo({
...courseInfo,
[name]: value,
});
};
// Handle adding a new course
const handleAddCourse = () => {
const { courseName, creditHours, grade } = courseInfo;
// Validate input fields
if (
courseName &&
parseFloat(creditHours) > 0 &&
grade
) {
// Create new course object
const newCourse = {
courseName,
creditHours: parseFloat(creditHours),
grade,
};
// Pass new course to parent handler
onAddCourse(newCourse);
// Reset form fields
setCourseInfo({
courseName: "",
creditHours: "0",
grade: "A+",
});
} else {
// Show alert if input is invalid
alert("Please enter valid course details.");
}
};
return (
<View style={styles.section}>
{/* Course name input */}
<Text style={styles.label}>Course</Text>
<TextInput
value={courseInfo.courseName}
onChangeText={(text) =>
handleInputChange("courseName", text)
}
style={styles.input}
/>
{/* Credit hours input */}
<Text style={styles.label}>Credits</Text>
<TextInput
value={courseInfo.creditHours}
onChangeText={(text) =>
handleInputChange("creditHours", text)
}
keyboardType="numeric"
style={styles.input}
/>
{/* Grade picker dropdown */}
<Text style={styles.label}>Grade</Text>
<RNPickerSelect
value={courseInfo.grade}
onValueChange={(value) =>
handleInputChange("grade", value)
}
style={styles.picker}
items={Object.keys(gradePoints).map(
(key) => ({
label: key,
value: key,
})
)}
/>
{/* Add course button */}
<CustomButton
title="Add"
onPress={handleAddCourse}
style={[
styles.addButton,
courseInfo.courseName &&
parseFloat(courseInfo.creditHours) > 0 &&
courseInfo.grade
? styles.addButtonHover
: null,
]}
/>
</View>
);
};
- CustomButton:
This component is used to make a custom button by taking title, onPress, and style, designed by wrapping TouchableOpacity with a View component which contains a style and a Text component as it's child.
// Custom button component for reusability
const CustomButton = ({ title, onPress, style }) => {
return (
<TouchableOpacity onPress={onPress}>
{/* Button container with custom styles */}
<View style={[styles.customButton, style]}>
{/* Button text */}
<Text style={styles.customButtonText}>
{title}
</Text>
</View>
</TouchableOpacity>
);
};
- CourseList:
This component shows a list of courses and your GPA. For each course, there is a delete button so you can remove any course you want. It will also calculate and display your GPA at the end.
// Component to display the list of courses and GPA
const CourseList = ({
courses,
onDeleteCourse,
calculateGPA,
}) => {
return (
<View style={styles.section}>
{/* Section header */}
<Text style={styles.header}>Course List</Text>
{/* Scrollable list of courses */}
<ScrollView style={styles.list}>
{/* List header row */}
<View style={styles.listHeader}>
<Text>Course</Text>
<Text>Credits</Text>
<Text>Grade</Text>
<Text>Action</Text>
</View>
{/* Render each course in the list */}
{courses.map((course, index) => (
<View
style={styles.listItem}
key={index}
>
<Text>{course.courseName}</Text>
<Text>{course.creditHours}</Text>
<Text>{course.grade}</Text>
{/* Delete button for each course */}
<TouchableOpacity
style={styles.deletebtn}
onPress={() =>
onDeleteCourse(index)
}
>
<Text
style={styles.deletebtntext}
>
Delete
</Text>
</TouchableOpacity>
</View>
))}
</ScrollView>
{/* Display calculated GPA */}
<Text style={styles.gpa}>
GPA: {calculateGPA().toFixed(2)}
</Text>
</View>
);
};
- Call Components:
Call the CourseForm and CourseList components in the App component to render.
{/* This code placed below the Title Text*/}
{/* Course input form */}
<CourseForm onAddCourse={addCourse} />
{/* List of courses and GPA */}
<CourseList
courses={courses}
onDeleteCourse={deleteCourse}
calculateGPA={calculateGPA}
/>
- addCourse, deleteCourse, calculateGPA, and courses:
These functions and states we have already used in the above code while calling components from the App components.
// State to hold all courses
const [courses, setCourses] = useState([]);
// Add a new course to the list
const addCourse = (newCourse) => {
setCourses([...courses, newCourse]);
};
// Delete a course by index
const deleteCourse = (index) => {
const updatedCourses = courses.filter(
(_course, i) => i !== index
);
setCourses(updatedCourses);
};
// Calculate GPA based on courses
const calculateGPA = () => {
let totalGradePoints = 0;
let totalCreditHours = 0;
// Sum up grade points and credit hours
courses.forEach((course) => {
totalGradePoints +=
gradePoints[course.grade] *
course.creditHours;
totalCreditHours += course.creditHours;
});
// Return GPA or 0 if no courses
return totalCreditHours === 0
? 0
: totalGradePoints / totalCreditHours;
};
Now, wrap all design code with a View component, return it from the App component, and place all methods and useStates within the App component. Ensure to export the App.
Complete Source Code
// Import useState hook from React
import { useState } from "react";
// Import core React Native UI components
import {
View, // Container component for layout
Text, // Component for displaying text
TextInput, // Input field for user text entry
TouchableOpacity, // Pressable component for buttons
ScrollView, // Scrollable container for lists
} from "react-native";
// Import picker select component for dropdowns
import RNPickerSelect from "react-native-picker-select";
// Import styles from external stylesheet
import { styles } from "./styles";
// Define grade points mapping for each grade
const gradePoints = {
"A+": 4.0,
A: 4.0,
"A-": 3.7,
"B+": 3.3,
B: 3.0,
"B-": 2.7,
"C+": 2.3,
C: 2.0,
"C-": 1.7,
"D+": 1.3,
D: 1.0,
"D-": 0.7,
};
// Custom button component for reusability
const CustomButton = ({ title, onPress, style }) => {
return (
<TouchableOpacity onPress={onPress}>
{/* Button container with custom styles */}
<View style={[styles.customButton, style]}>
{/* Button text */}
<Text style={styles.customButtonText}>
{title}
</Text>
</View>
</TouchableOpacity>
);
};
// Form component to add a new course
const CourseForm = ({ onAddCourse }) => {
// State to hold course input values
const [courseInfo, setCourseInfo] = useState({
courseName: "",
creditHours: "0",
grade: "A+",
});
// Handle input changes for form fields
const handleInputChange = (name, value) => {
setCourseInfo({
...courseInfo,
[name]: value,
});
};
// Handle adding a new course
const handleAddCourse = () => {
const { courseName, creditHours, grade } = courseInfo;
// Validate input fields
if (
courseName &&
parseFloat(creditHours) > 0 &&
grade
) {
// Create new course object
const newCourse = {
courseName,
creditHours: parseFloat(creditHours),
grade,
};
// Pass new course to parent handler
onAddCourse(newCourse);
// Reset form fields
setCourseInfo({
courseName: "",
creditHours: "0",
grade: "A+",
});
} else {
// Show alert if input is invalid
alert("Please enter valid course details.");
}
};
return (
<View style={styles.section}>
{/* Course name input */}
<Text style={styles.label}>Course</Text>
<TextInput
value={courseInfo.courseName}
onChangeText={(text) =>
handleInputChange("courseName", text)
}
style={styles.input}
/>
{/* Credit hours input */}
<Text style={styles.label}>Credits</Text>
<TextInput
value={courseInfo.creditHours}
onChangeText={(text) =>
handleInputChange("creditHours", text)
}
keyboardType="numeric"
style={styles.input}
/>
{/* Grade picker dropdown */}
<Text style={styles.label}>Grade</Text>
<RNPickerSelect
value={courseInfo.grade}
onValueChange={(value) =>
handleInputChange("grade", value)
}
style={styles.picker}
items={Object.keys(gradePoints).map(
(key) => ({
label: key,
value: key,
})
)}
/>
{/* Add course button */}
<CustomButton
title="Add"
onPress={handleAddCourse}
style={[
styles.addButton,
courseInfo.courseName &&
parseFloat(courseInfo.creditHours) > 0 &&
courseInfo.grade
? styles.addButtonHover
: null,
]}
/>
</View>
);
};
// Component to display the list of courses and GPA
const CourseList = ({
courses,
onDeleteCourse,
calculateGPA,
}) => {
return (
<View style={styles.section}>
{/* Section header */}
<Text style={styles.header}>Course List</Text>
{/* Scrollable list of courses */}
<ScrollView style={styles.list}>
{/* List header row */}
<View style={styles.listHeader}>
<Text>Course</Text>
<Text>Credits</Text>
<Text>Grade</Text>
<Text>Action</Text>
</View>
{/* Render each course in the list */}
{courses.map((course, index) => (
<View
style={styles.listItem}
key={index}
>
<Text>{course.courseName}</Text>
<Text>{course.creditHours}</Text>
<Text>{course.grade}</Text>
{/* Delete button for each course */}
<TouchableOpacity
style={styles.deletebtn}
onPress={() =>
onDeleteCourse(index)
}
>
<Text
style={styles.deletebtntext}
>
Delete
</Text>
</TouchableOpacity>
</View>
))}
</ScrollView>
{/* Display calculated GPA */}
<Text style={styles.gpa}>
GPA: {calculateGPA().toFixed(2)}
</Text>
</View>
);
};
// Main App component
const App = () => {
// State to hold all courses
const [courses, setCourses] = useState([]);
// Add a new course to the list
const addCourse = (newCourse) => {
setCourses([...courses, newCourse]);
};
// Delete a course by index
const deleteCourse = (index) => {
const updatedCourses = courses.filter(
(_course, i) => i !== index
);
setCourses(updatedCourses);
};
// Calculate GPA based on courses
const calculateGPA = () => {
let totalGradePoints = 0;
let totalCreditHours = 0;
// Sum up grade points and credit hours
courses.forEach((course) => {
totalGradePoints +=
gradePoints[course.grade] *
course.creditHours;
totalCreditHours += course.creditHours;
});
// Return GPA or 0 if no courses
return totalCreditHours === 0
? 0
: totalGradePoints / totalCreditHours;
};
return (
<View style={styles.container}>
{/* App title */}
<Text style={styles.title}>GPA Calculator</Text>
{/* Course input form */}
<CourseForm onAddCourse={addCourse} />
{/* List of courses and GPA */}
<CourseList
courses={courses}
onDeleteCourse={deleteCourse}
calculateGPA={calculateGPA}
/>
</View>
);
};
// Export the main App component as default
export default App;
// Import the StyleSheet module from react-native for creating styles
import { StyleSheet } from "react-native";
// Create a StyleSheet object containing all styles for the app
const styles = StyleSheet.create({
// Container style for the main layout
container: {
flex: 1, // Take up the full available space
padding: 16, // Add padding inside the container
backgroundColor: "#f0f0f0", // Set background color
},
// Style for the main title text
title: {
fontSize: 25, // Set font size
fontWeight: "bold", // Make text bold
marginBottom: 10, // Space below the title
textAlign: "center", // Center the text
color: "#333", // Set text color
margin: 10, // Margin on all sides
paddingTop: 30, // Padding at the top
},
// Style for each section/card
section: {
marginBottom: 16, // Space below the section
backgroundColor: "#fff", // Section background color
padding: 20, // Padding inside the section
borderRadius: 5, // Rounded corners
borderWidth: 1, // Border width
borderColor: "#ced4da", // Border color
shadowColor: "grey", // Shadow color (iOS)
shadowOffset: {
width: 0, // Shadow offset horizontally
height: 6, // Shadow offset vertically
},
shadowOpacity: 1, // Shadow opacity (iOS)
shadowRadius: 15, // Shadow blur radius (iOS)
elevation: 5, // Shadow elevation (Android)
},
// Style for labels
label: {
fontSize: 16, // Font size
marginBottom: 8, // Space below the label
color: "#333", // Text color
},
// Style for input fields
input: {
borderColor: "#ced4da", // Border color
borderWidth: 1, // Border width
borderRadius: 4, // Rounded corners
padding: 8, // Padding inside input
marginBottom: 8, // Space below input
fontSize: 16, // Font size
color: "#333", // Text color
},
// Style for headers
header: {
fontSize: 18, // Font size
fontWeight: "bold", // Bold text
marginBottom: 10, // Space below header
textAlign: "center", // Center the text
color: "#333", // Text color
},
// Style for the list container
list: {
maxHeight: 200, // Maximum height for the list
},
// Style for the list header row
listHeader: {
flexDirection: "row", // Arrange children in a row
justifyContent: "space-between", // Space between items
borderBottomWidth: 1, // Bottom border width
borderColor: "#ccc", // Border color
marginBottom: 10, // Space below header
paddingBottom: 10, // Padding below header
},
// Style for each list item row
listItem: {
flexDirection: "row", // Arrange children in a row
justifyContent: "space-between", // Space between items
borderBottomWidth: 1, // Bottom border width
borderColor: "#ccc", // Border color
marginBottom: 8, // Space below item
paddingBottom: 8, // Padding below item
},
// Style for GPA display
gpa: {
fontSize: 20, // Font size
fontWeight: "bold", // Bold text
marginTop: 10, // Space above GPA
textAlign: "center", // Center the text
color: "#333", // Text color
},
// Style for the add button
addButton: {
padding: 9.5, // Padding inside button
backgroundColor: "#fff", // Button background color
color: "#1d9bf0", // Text color
borderWidth: 1, // Border width
borderColor: "#1d9bf0", // Border color
borderRadius: 4, // Rounded corners
fontWeight: "bold", // Bold text
transition: "0.5s all", // Transition effect (not supported in React Native)
textAlign: "center", // Center the text
alignContent: "center", // Align content vertically
alignItems: "center", // Align items horizontally
justifyContent: "center", // Center content
},
// Style for add button when hovered (not supported in React Native)
addButtonHover: {
color: "white", // Text color on hover
backgroundColor: "#1d9bf0", // Background color on hover
borderColor: "#1d9bf0", // Border color on hover
borderWidth: 1, // Border width on hover
},
// Style for delete button
deletebtn: {
padding: 9.5, // Padding inside button
color: "#1d9bf0", // Text color
fontWeight: "bold", // Bold text
textAlign: "center", // Center the text
backgroundColor: "red", // Button background color
borderRadius: 15, // Rounded corners
},
// Style for delete button text
deletebtntext: {
color: "white", // Text color
},
// Style for picker component
picker: {
fontSize: 16, // Font size
paddingVertical: 12, // Vertical padding
paddingHorizontal: 10, // Horizontal padding
borderWidth: 1, // Border width
borderColor: "gray", // Border color
borderRadius: 4, // Rounded corners
color: "black", // Text color
paddingRight: 30, // Padding on the right
},
});
// Export the styles object for use in other files
export { styles };