Create a Event Countdown App with React Native
In this article, we will create an event countdown time application using React Native. The Event Countdown App is a mobile application developed with React Native. It enables users to effortlessly create and monitor countdown timers for different events.
By providing the event title, selecting a category, and specifying the date and time, users can set up their desired countdown. The app then showcases a timer that displays the remaining time until the event starts. Once the specified event time is reached, the timer will cease counting down, allowing users to promptly remove the countdown if needed.
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
Approach:
- To manage state, we will use the useState and useEffect hooks and use them to maintain different variables. In the application, timers in the state are organized as an array of objects where each timer object encompasses event details, category, target date and time, remaining time.
- The useEffect hook dynamically updates the time remaining for timers based on the current time. It then configures interval timers to run these timers and marks them as inactive when they reach zero.
- The app utilizes the react-native-modal library to display a modal for selecting categories and the react-native-modal-datetime-picker for choosing dates. In addition, it provides brief messages to confirm successful category or date selections, which disappear after a second.
- Users can easily create timers by specifying a title, category, and date. To ensure accuracy, the addTimer function not only verifies the input but also calculates the remaining time before appending the new timer
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: Update Dependencies
The updated dependencies in the package.json file will look like below:
"dependencies": {
"react-native-paper": "4.9.2",
"@expo/vector-icons": "^13.0.0",
"react-native-modal-datetime-picker": "*",
"@react-native-community/datetimepicker": "6.7.3",
"react-native-modal": "*"
}
Step 4: Start Coding
Example: In this example, we are using the above-explained approach to create the event countdown app.
- App.js: This file imports all the components and renders them on the screen.
- Style.js: This file adds styling to the application.
// App.js
import React, { useState, useEffect } from "react";
import {
View,
Text,
TextInput,
ScrollView,
StyleSheet,
TouchableOpacity,
} from "react-native";
import DateTimePickerModal from "react-native-modal-datetime-picker";
import Modal from "react-native-modal";
import { styles } from "./Style";
const categoryColors = {
Meeting: "#0984e3",
Birthday: "#00b894",
Reminder: "#00cec9",
};
function App() {
const [timers, setTimers] = useState([]);
const [newTimerTitle, setNewTimerTitle] = useState("");
const [newTimerCategory, setNewTimerCategory] = useState("");
const [newTimerDateTime, setNewTimerDateTime] = useState("");
const [isDatePickerVisible, setDatePickerVisibility] = useState(false);
const [timerRunning, setTimerRunning] = useState(false);
const [selectedDate, setSelectedDate] = useState(new Date());
const [isModalVisible, setModalVisible] = useState(false);
const [selectedCategory, setSelectedCategory] = useState("");
const [categorySelectedMessage, setCategorySelectedMessage] = useState("");
const [dateSelectedMessage, setDateSelectedMessage] = useState("");
useEffect(() => {
const intervalIds = {};
const updateTimers = () => {
setTimers((prevTimers) =>
prevTimers.map((timer) => {
const targetTime = new Date(timer.targetDateTime).getTime();
const currentTime = new Date().getTime();
const timeRemaining = Math.max(
Math.floor((targetTime - currentTime) / 1000),
0
);
if (timeRemaining === 0) {
clearInterval(intervalIds[timer.id]);
return { ...timer, isRunning: false, timeRemaining: 0 };
}
return { ...timer, timeRemaining };
})
);
};
timers.forEach((timer) => {
if (timer.isRunning && timer.timeRemaining > 0 && timerRunning) {
intervalIds[timer.id] = setInterval(updateTimers, 1000);
}
});
return () => {
Object.values(intervalIds).forEach((intervalId) =>
clearInterval(intervalId)
);
};
}, [timers, timerRunning]);
const removeTimer = (timerId) => {
setTimers((prevTimers) =>
prevTimers.filter((timer) => timer.id !== timerId)
);
};
const calculateTimeRemaining = (targetTime) => {
const currentTime = new Date().getTime();
const timeDifference = targetTime - currentTime;
const secondsRemaining = Math.max(Math.floor(timeDifference / 1000), 0);
return secondsRemaining;
};
const formatTimeRemaining = (seconds) => {
const days = Math.floor(seconds / (3600 * 24));
const hours = Math.floor((seconds % (3600 * 24)) / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
const remainingSeconds = seconds % 60;
return {
days,
hours,
minutes,
seconds: remainingSeconds,
};
};
const showDatePicker = () => {
setDatePickerVisibility(true);
};
const hideDatePicker = () => {
setDatePickerVisibility(false);
};
const handleConfirm = (date) => {
setSelectedDate(date);
setNewTimerDateTime(date.toISOString());
setDateSelectedMessage("Date has been selected successfully");
hideDatePicker();
setTimeout(() => {
setDateSelectedMessage("");
}, 1000);
};
const toggleModal = () => {
setModalVisible(!isModalVisible);
};
const handleCategorySelection = (category) => {
setSelectedCategory(category);
setCategorySelectedMessage("Category has been selected successfully");
setModalVisible(false);
setTimeout(() => {
setCategorySelectedMessage("");
}, 1000);
};
const addTimer = () => {
if (!newTimerTitle || !selectedCategory || !newTimerDateTime) return;
const targetDateTime = new Date(newTimerDateTime).getTime();
const newTimer = {
id: timers.length + 1,
category: selectedCategory,
targetDateTime,
timeRemaining: calculateTimeRemaining(targetDateTime),
isRunning: true,
title: newTimerTitle,
showTitleInput: false,
};
setTimers([...timers, newTimer]);
setNewTimerTitle("");
setNewTimerCategory("");
setNewTimerDateTime("");
setTimerRunning(true);
};
return (
<ScrollView style={styles.container}>
<View style={styles.inputContainer}>
<Text style={styles.heading}>Event Countdown Timer</Text>
<TextInput
style={styles.input}
placeholder="Timer Title"
value={newTimerTitle}
onChangeText={(text) => setNewTimerTitle(text)}
/>
<TouchableOpacity
style={styles.button}
onPress={toggleModal}
disabled={!newTimerTitle}
>
<Text style={styles.buttonText}>Select Category</Text>
</TouchableOpacity>
<Modal isVisible={isModalVisible}>
<View style={styles.modalContainer}>
<Text style={styles.modalTitle}>Select a Category</Text>
<TouchableOpacity
style={styles.categoryButton}
onPress={() => handleCategorySelection("Meeting")}
>
<Text style={styles.buttonText}>Meeting</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.categoryButton}
onPress={() => handleCategorySelection("Birthday")}
>
<Text style={styles.buttonText}>Birthday</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.categoryButton}
onPress={() => handleCategorySelection("Reminder")}
>
<Text style={styles.buttonText}>Reminder</Text>
</TouchableOpacity>
</View>
</Modal>
<Text style={styles.messageText}>
{categorySelectedMessage}
</Text>
<TouchableOpacity
style={styles.button}
onPress={showDatePicker}
disabled={!newTimerTitle || !selectedCategory}
>
<Text style={styles.buttonText}>Select Date</Text>
</TouchableOpacity>
<DateTimePickerModal
isVisible={isDatePickerVisible}
mode="datetime"
onConfirm={handleConfirm}
onCancel={hideDatePicker}
/>
<Text style={styles.messageText}>{dateSelectedMessage}</Text>
<TouchableOpacity
style={styles.button}
onPress={addTimer}
disabled={
!newTimerTitle || !selectedCategory || !newTimerDateTime
}
>
<Text style={styles.buttonText}>Add Timer</Text>
</TouchableOpacity>
</View>
<View style={styles.timersContainer}>
{timers.map((timer) => {
const timeRemaining = formatTimeRemaining(
timer.timeRemaining
);
return (
<View
key={timer.id}
style={[
styles.card,
{
backgroundColor:
categoryColors[timer.category] ||
"transparent",
},
]}
>
<Text style={styles.cardTitle}>{timer.title}</Text>
<Text style={styles.cardCategory}>
{timer.category}
</Text>
<View style={styles.timerInfo}>
{timeRemaining.days > 0 && (
<View style={styles.timeInfo}>
<Text>
<Text style={styles.timeValue}>
{timeRemaining.days}
</Text>{" "}
days
</Text>
</View>
)}
<View style={styles.timeInfo}>
<Text>
<Text style={styles.timeValue}>
{timeRemaining.hours}
</Text>{" "}
hours
</Text>
</View>
<View style={styles.timeInfo}>
<Text>
<Text style={styles.timeValue}>
{timeRemaining.minutes}
</Text>{" "}
minutes
</Text>
</View>
<View style={styles.timeInfo}>
<Text>
<Text style={styles.timeValue}>
{timeRemaining.seconds}
</Text>{" "}
seconds
</Text>
</View>
</View>
<TouchableOpacity
style={styles.button}
onPress={() => removeTimer(timer.id)}
disabled={timer.timeRemaining <= 0}
>
<Text style={styles.buttonText}>Remove</Text>
</TouchableOpacity>
</View>
);
})}
</View>
</ScrollView>
);
}
export default App;
// styles.js
import { StyleSheet } from "react-native";
const styles = StyleSheet.create({
container: {
flex: 1,
},
inputContainer: {
margin: 20,
},
heading: {
fontSize: 30,
textAlign: "center",
color: "#0984e3",
margin: 20,
fontWeight: "bold",
},
input: {
height: 40,
borderColor: "gray",
borderWidth: 1,
marginBottom: 10,
borderRadius: 5,
paddingLeft: 10,
},
timersContainer: {
margin: 10,
alignItems: "center",
},
card: {
margin: 10,
padding: 10,
borderRadius: 10,
shadowColor: "black",
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.3,
shadowRadius: 4,
elevation: 5,
backgroundColor: "white",
},
cardTitle: {
fontSize: 25,
color: "white",
marginBottom: 5,
fontWeight: "bold",
},
cardCategory: {
fontSize: 20,
color: "#2d3436",
marginBottom: 5,
fontWeight: "bold",
},
timerInfo: {
flexDirection: "row",
alignItems: "center",
},
timeInfo: {
backgroundColor: "lightgray",
borderRadius: 5,
padding: 5,
margin: 2,
},
timeValue: {
fontWeight: "bold",
},
button: {
backgroundColor: "#0984e3",
padding: 10,
borderRadius: 5,
marginTop: 5,
marginBottom: 10,
},
buttonText: {
color: "white",
textAlign: "center",
},
modalContainer: {
backgroundColor: "white",
padding: 20,
borderRadius: 10,
shadowColor: "black",
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 1,
shadowRadius: 4,
elevation: 5,
},
modalTitle: {
fontSize: 18,
marginBottom: 10,
},
categoryButton: {
backgroundColor: "#0984e3",
padding: 10,
borderRadius: 5,
marginBottom: 10,
},
messageText: {
color: "green",
textAlign: "center",
marginTop: 10,
},
});
export { styles };