Hey devs, welcome to this tutorial; we will build a real-time Todo App with React, NextJs + Firebase. The full code of this project is available in this repository The explanation of this article is in this video Before we start writing code, I want to tell you that I expect you to have basic knowledge of React.Js and Next.Js If you are not familiar with React and Next, go through these documentations ReactJs Documentation NextJs Documentation Here’s what we are going to build After completing this tutorial, our app will look like this Here’s our tech stack for this project : We will ReactJs for building the UI of this app. ReactJs : NextJs is a react-based framework for building modern web apps. It provides benefits like and many benefits NextJs Server Side Rendering SEO : We will use firebase as a backend of this app. We will store data in **.** It provides many functions for creating real-time apps. (Our todo app will be real-time) Firebase Firestore : ChakraUI is a styling framework. It has styled components ready to be used in-app. It provides responsiveness, dark mode, an eye-appealing color scheme, and so on. ChakraUI Why are we using Firebase in our project? Firebase provides so much functionality that it is hard to ignore. It provides Social Auth with just 4–5 lines of code. We will implement in our Project using firebase Google Auth Some features of Firebase are: Authentication Real-Time Server Low-Security Risk Social Auth Minimal Setup Table of Contents Create Firebase Project Create Methods to Interact with Firestore Create React Components (Add Todo, Auth, TodoList) npm run dev 😁 Let’s begin with creating the next app Type the following command to install the next app npx create-next-app next-firebase-todo Install the required dependencies Chakra UI npm i @chakra-ui/react @emotion/react @emotion/styled framer-motion 2. Firebase npm i firebase Create Firebase Project Go to Firebase Console Click on Create a Project Name your project and tick the box, and Click Continue We don’t need Google Analytics for the Project. So, untoggle that button and click Continue After your project has been created, you will see your project Dashboard As Icon suggests, the first button is for the project, the second for and the third for IOS Android, Web Apps. We are creating a web app, so we are gonna click on that. Name your web app and click Register app Copy this code and click on Continue to Console : Save this code somewhere. We will need it later in our project. Note On the left side, click on Build We will only need two features of firebase from this list. — For adding Google Sign In — For Storing User Todos Authentication FireStore Database Now, let's Enable these two features first. Click on Authentication Click Get Started We are only gonna use Google Authentication, so click on Google Enable Google Authentication, Select Project Support Email, and click Save We have successfully enabled Google Auth in our Firebase Project Now let us enable the Firestore Database. Go to the left Sidebar, Click on Build, and Click Firestore Database Click on Create database Select and click and Click start in test mode Next, Enable Now we have enabled both and the required for our Project Authentication Firestore Database Now let’s start coding Let’s go to and copy-paste the following pages/_app.js import { ChakraProvider } from "@chakra-ui/react"; function MyApp({ Component, pageProps }) { return ( <ChakraProvider> <Component {...pageProps} /> </ChakraProvider> ); } export default MyApp; We just wrapped inside <Component {…pageProps} /> <ChakraProvider> Chakra UI’s official Documentation suggests this. Now we are going to set up firebase in our web app. Let us create a new folder at the project root named firebase Create a file named inside directory index.js firebase Now paste the code I told you to copy earlier(firebase config) import { initializeApp } from "firebase/app"; import { getAuth } from "firebase/auth"; import { getFirestore } from "firebase/firestore"; // replace this firebase conFigvariable with your own const firebaseConfig = { apiKey: "AIzaSyClH1YdssQHAA0peMtQ_wj2A4Crnc4fEgU", authDomain: "medium-firebase-next-todo.firebaseapp.com", projectId: "medium-firebase-next-todo", storageBucket: "medium-firebase-next-todo.appspot.com", messagingSenderId: "989711683222", appId: "1:989711683222:web:bc878dca5a5d251177fcb7", }; const app = initializeApp(firebaseConfig); const auth = getAuth(app); const db = getFirestore(app); export { auth, db }; What we did here is we just pasted the firebase config. We additionally created two variables and which are the and respectively. auth db firebase auth module firestore module, Let’s add a new dependency to our project, which is react-icons npm install react-icons --save React Icons library lets you add beautiful icons to your project. Now let us create methods to create and interact with todos Methods to Interact with Firestore Create a directory at the root of your project named api and create a file todo.js and copy and paste the following code in todo.js import { db } from "../firebase"; import { collection, addDoc, updateDoc, doc, deleteDoc, } from "firebase/firestore"; const addTodo = async ({ userId, title, description, status }) => { try { await addDoc(collection(db, "todo"), { user: userId, title: title, description: description, status: status, createdAt: new Date().getTime(), }); } catch (err) {} }; const toggleTodoStatus = async ({ docId, status }) => { try { const todoRef = doc(db, "todo", docId); await updateDoc(todoRef, { status, }); } catch (err) { console.log(err); } }; const deleteTodo = async (docId) => { try { const todoRef = doc(db, "todo", docId); await deleteDoc(todoRef); } catch (err) { console.log(err); } }; export { addTodo, toggleTodoStatus, deleteTodo }; Now let’s create an Authentication hook for checking auth status of the user. Create a directory named hooks and create a file in directory useAuth.js hooks and copy and paste the following code in useAuth.js import { useEffect, useState } from "react"; import { auth } from "../firebase"; const useAuth = () => { const [user, setUser] = useState(null); const [isLoggedIn, setIsLoggedIn] = useState(false); useEffect(() => { auth.onAuthStateChanged((user) => { setIsLoggedIn(user && user.uid ? true : false); setUser(user); }); }); return { user, isLoggedIn }; }; export default useAuth; What we did is created a useAuth hook that returns the current and Boolean. user isLoggedIn Every time the auth status changes, the method inside the is called. onAuthStateChanged This functionality is given by firebase itself Now let’s create UI Create React Components Create directory at the root of your web app components We will create 3 components in our app AddTodo — for adding a new todo Auth — for adding Login/Logout Button TodoList — for viewing all existing todos properly Let’s start with AddTodo. Create inside directory AddTodo.jsx components Copy and paste the following code inside AddTodo.jsx import React from "react"; import { Box, Input, Button, Textarea, Stack, Select, useToast, } from "@chakra-ui/react"; import useAuth from "../hooks/useAuth"; import { addTodo } from "../api/todo"; const AddTodo = () => { const [title, setTitle] = React.useState(""); const [description, setDescription] = React.useState(""); const [status, setStatus] = React.useState("pending"); const [isLoading, setIsLoading] = React.useState(false); const toast = useToast(); const { isLoggedIn, user } = useAuth(); const handleTodoCreate = async () => { if (!isLoggedIn) { toast({ title: "You must be logged in to create a todo", status: "error", duration: 9000, isClosable: true, }); return; } setIsLoading(true); const todo = { title, description, status, userId: user.uid, }; await addTodo(todo); setIsLoading(false); setTitle(""); setDescription(""); setStatus("pending"); toast({ title: "Todo created successfully", status: "success" }); }; return ( <Box w="40%" margin={"0 auto"} display="block" mt={5}> <Stack direction="column"> <Input placeholder="Title" value={title} onChange={(e) => setTitle(e.target.value)} /> <Textarea placeholder="Description" value={description} onChange={(e) => setDescription(e.target.value)} /> <Select value={status} onChange={(e) => setStatus(e.target.value)}> <option value={"pending"} style={{ color: "yellow", fontWeight: "bold" }} > Pending ⌛ </option> <option value={"completed"} style={{ color: "green", fontWeight: "bold" }} > Completed ✅ </option> </Select> <Button onClick={() => handleTodoCreate()} disabled={title.length < 1 || description.length < 1 || isLoading} variantColor="teal" variant="solid" > Add </Button> </Stack> </Box> ); }; export default AddTodo; Now create inside directory Auth.jsx component and copy and paste the following code inside it import React from "react"; import { Box, Button, Link, Text, useColorMode } from "@chakra-ui/react"; import { signInWithPopup, GoogleAuthProvider } from "firebase/auth"; import { FaGoogle, FaMoon, FaSun } from "react-icons/fa"; import { auth } from "../firebase"; import useAuth from "../hooks/useAuth"; const Auth = () => { const { toggleColorMode, colorMode } = useColorMode(); const { isLoggedIn, user } = useAuth(); const handleAuth = async () => { const provider = new GoogleAuthProvider(); signInWithPopup(auth, provider) .then((result) => { // This gives you a Google Access Token. You can use it to access the Google API. const credential = GoogleAuthProvider.credentialFromResult(result); const token = credential.accessToken; // The signed-in user info. const user = result.user; // ... }) .catch((error) => { // Handle Errors here. const errorCode = error.code; const errorMessage = error.message; // The email of the user's account used. const email = error.customData.email; // The AuthCredential type that was used. const credential = GoogleAuthProvider.credentialFromError(error); // ... }); }; return ( <Box position={"fixed"} top="5%" right="5%"> <Button onClick={() => toggleColorMode()}> {colorMode == "dark" ? <FaSun /> : <FaMoon />} </Button>{" "} {isLoggedIn && ( <> <Text color="green.500">{user.email}</Text> <Link color="red.500" onClick={() => auth.signOut()}> Logout </Link> </> )} {!isLoggedIn && ( <Button leftIcon={<FaGoogle />} onClick={() => handleAuth()}> Login with Google </Button> )} </Box> ); }; export default Auth; Now let’s create the component inside directory. TodoList.jsx components And copy and paste the following code inside it import { Badge, Box, Heading, SimpleGrid, Text, useToast, } from "@chakra-ui/react"; import React, { useEffect } from "react"; import useAuth from "../hooks/useAuth"; import { collection, onSnapshot, query, where } from "firebase/firestore"; import { db } from "../firebase"; import { FaToggleOff, FaToggleOn, FaTrash } from "react-icons/fa"; import { deleteTodo, toggleTodoStatus } from "../api/todo"; const TodoList = () => { const [todos, setTodos] = React.useState([]); const { user } = useAuth(); const toast = useToast(); const refreshData = () => { if (!user) { setTodos([]); return; } const q = query(collection(db, "todo"), where("user", "==", user.uid)); onSnapshot(q, (querySnapchot) => { let ar = []; querySnapchot.docs.forEach((doc) => { ar.push({ id: doc.id, ...doc.data() }); }); setTodos(ar); }); }; useEffect(() => { refreshData(); }, [user]); const handleTodoDelete = async (id) => { if (confirm("Are you sure you wanna delete this todo?")) { deleteTodo(id); toast({ title: "Todo deleted successfully", status: "success" }); } }; const handleToggle = async (id, status) => { const newStatus = status == "completed" ? "pending" : "completed"; await toggleTodoStatus({ docId: id, status: newStatus }); toast({ title: `Todo marked ${newStatus}`, status: newStatus == "completed" ? "success" : "warning", }); }; return ( <Box mt={5}> <SimpleGrid columns={{ base: 1, md: 3 }} spacing={8}> {todos && todos.map((todo) => ( <Box p={3} boxShadow="2xl" shadow={"dark-lg"} transition="0.2s" _hover={{ boxShadow: "sm" }} > <Heading as="h3" fontSize={"xl"}> {todo.title}{" "} <Badge color="red.500" bg="inherit" transition={"0.2s"} _hover={{ bg: "inherit", transform: "scale(1.2)", }} float="right" size="xs" onClick={() => handleTodoDelete(todo.id)} > <FaTrash /> </Badge> <Badge color={todo.status == "pending" ? "gray.500" : "green.500"} bg="inherit" transition={"0.2s"} _hover={{ bg: "inherit", transform: "scale(1.2)", }} float="right" size="xs" onClick={() => handleToggle(todo.id, todo.status)} > {todo.status == "pending" ? <FaToggleOff /> : <FaToggleOn />} </Badge> <Badge float="right" opacity="0.8" bg={todo.status == "pending" ? "yellow.500" : "green.500"} > {todo.status} </Badge> </Heading> <Text>{todo.description}</Text> </Box> ))} </SimpleGrid> </Box> ); }; export default TodoList; We have created all the required components, now we are gonna place them in our app. Go to and remove all the contents and paste the following content pages/index.js import { Container } from "@chakra-ui/react"; import AddTodo from "../components/AddTodo"; import Auth from "../components/Auth"; import TodoList from "../components/TodoList"; export default function Home() { return ( <Container maxW="7xl"> <Auth /> <AddTodo /> <TodoList /> </Container> ); } And we are done. npm run dev We have completed the coding of the app. Go to your terminal and type npm run dev and goto in your browser localhost:3000 You will see something like this Now you can and start adding Todo’s log in with Google Get the full code here Watch the explanation video here Follow me on Twitter Also Published Here