Build a Simple Next.js Interactive To-Do List App

Written by

in

Are you tired of juggling multiple apps or sticky notes to keep track of your daily tasks? In today’s fast-paced world, staying organized is crucial. A well-designed to-do list app can be a game-changer, helping you prioritize tasks, manage your time effectively, and ultimately, boost your productivity. In this tutorial, we’ll dive into building a simple, yet functional, interactive to-do list application using Next.js, a powerful React framework.

Why Build a To-Do List App?

Building a to-do list app isn’t just a fun project; it’s a practical way to learn and apply fundamental web development concepts. You’ll gain hands-on experience with:

  • State Management: Understanding how to store and update data within your application.
  • Event Handling: Learning how to respond to user interactions, such as adding, deleting, and marking tasks as complete.
  • Component Composition: Building reusable UI elements to create a modular and maintainable application.
  • User Interface (UI) Design: Creating an intuitive and user-friendly interface.

Moreover, this project provides a solid foundation for more complex web applications. The skills you acquire here are transferable and can be applied to a wide range of projects.

Prerequisites

Before we begin, ensure you have the following:

  • Node.js and npm (or yarn): Installed on your computer. You can download these from the official Node.js website.
  • A Code Editor: Such as Visual Studio Code, Sublime Text, or Atom.
  • Basic Understanding of HTML, CSS, and JavaScript: Familiarity with these languages will be helpful, but we’ll explain the key concepts as we go.

Setting Up Your Next.js Project

Let’s get started by creating a new Next.js project. Open your terminal and run the following command:

npx create-next-app todo-app

This command will create a new directory called todo-app with all the necessary files for a Next.js project. Navigate into the project directory:

cd todo-app

Now, start the development server:

npm run dev

Open your web browser and go to http://localhost:3000. You should see the default Next.js welcome page. This confirms that your project is set up correctly.

Project Structure Overview

Before diving into the code, let’s take a quick look at the project structure. The key files and directories we’ll be working with are:

  • pages/index.js: This is the main page of our application (the homepage). We’ll write the code for our to-do list here.
  • components/: We’ll create this directory to store reusable UI components.
  • styles/globals.css: This file contains global styles for our application.

Building the To-Do List Components

Let’s start by creating the components for our to-do list. We’ll need two main components: a ToDoInput component for adding new tasks and a ToDoItem component for displaying each task.

Creating the ToDoInput Component

Create a new file named ToDoInput.js inside the components directory. Add the following code:

// components/ToDoInput.js
import { useState } from 'react';

function ToDoInput({ onAddTodo }) {
 const [inputValue, setInputValue] = useState('');

 const handleInputChange = (event) => {
 setInputValue(event.target.value);
 };

 const handleAddTodo = () => {
 if (inputValue.trim() !== '') {
 onAddTodo(inputValue);
 setInputValue('');
 }
 };

 return (
 <div className="todo-input">
 <input
 type="text"
 value={inputValue}
 onChange={handleInputChange}
 placeholder="Add a task..."
 />
 <button onClick={handleAddTodo}>Add</button>
 </div>
 );
}

export default ToDoInput;

Explanation:

  • We import the useState hook to manage the input value.
  • inputValue stores the text entered by the user.
  • handleInputChange updates inputValue as the user types.
  • handleAddTodo calls the onAddTodo function (passed as a prop) with the input value and clears the input field.

Creating the ToDoItem Component

Create a new file named ToDoItem.js inside the components directory. Add the following code:

// components/ToDoItem.js
function ToDoItem({ todo, onDeleteTodo, onToggleComplete }) {
 return (
 <div className="todo-item">
 <input
 type="checkbox"
 checked={todo.completed}
 onChange={() => onToggleComplete(todo.id)}
 />
 <span className={todo.completed ? 'completed' : ''}>{todo.text}</span>
 <button onClick={() => onDeleteTodo(todo.id)}>Delete</button>
 </div>
 );
}

export default ToDoItem;

Explanation:

  • This component receives a todo object (containing id, text, and completed), onDeleteTodo, and onToggleComplete as props.
  • It renders a checkbox to mark the task as complete, a text span to display the task, and a delete button.
  • The completed class is added to the span if the task is marked as complete (we’ll define this class in our CSS).

Implementing the To-Do List Logic in index.js

Now, let’s integrate these components into our main page (pages/index.js) and implement the core to-do list functionality.

Replace the content of pages/index.js with the following code:

// pages/index.js
import { useState } from 'react';
import ToDoInput from '../components/ToDoInput';
import ToDoItem from '../components/ToDoItem';

function HomePage() {
 const [todos, setTodos] = useState([]);

 const addTodo = (text) => {
 const newTodo = {
 id: Date.now(), // Generate a unique ID
 text: text,
 completed: false,
 };
 setTodos([...todos, newTodo]);
 };

 const deleteTodo = (id) => {
 setTodos(todos.filter((todo) => todo.id !== id));
 };

 const toggleComplete = (id) => {
 setTodos(
 todos.map((todo) =>
 todo.id === id ? { ...todo, completed: !todo.completed } : todo
 )
 );
 };

 return (
 <div className="container">
 <h1>My To-Do List</h1>
 <ToDoInput onAddTodo={addTodo} />
 <div className="todo-list">
 {todos.map((todo) => (
 <ToDoItem
 key={todo.id}
 todo={todo}
 onDeleteTodo={deleteTodo}
 onToggleComplete={toggleComplete}
 />
 ))}
 </div>
 </div>
 );
}

export default HomePage;

Explanation:

  • We import useState, ToDoInput, and ToDoItem.
  • todos is an array that stores our to-do items. We initialize it as an empty array.
  • addTodo creates a new to-do object with a unique ID, the text from the input, and a completed status (initially false). It then updates the todos state by adding the new todo.
  • deleteTodo filters out the to-do item with the specified ID.
  • toggleComplete toggles the completed status of a to-do item.
  • We render the ToDoInput component and pass the addTodo function as a prop.
  • We map through the todos array and render a ToDoItem component for each to-do item, passing the necessary props (todo, onDeleteTodo, and onToggleComplete).

Styling Your To-Do List

To make our to-do list look presentable, let’s add some CSS. Open styles/globals.css and add the following styles:

/* styles/globals.css */
body {
 font-family: sans-serif;
 margin: 0;
 padding: 20px;
 background-color: #f4f4f4;
}

.container {
 max-width: 600px;
 margin: 0 auto;
 background-color: #fff;
 padding: 20px;
 border-radius: 8px;
 box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}

h1 {
 text-align: center;
 color: #333;
}

.todo-input {
 display: flex;
 margin-bottom: 20px;
}

.todo-input input {
 flex-grow: 1;
 padding: 10px;
 border: 1px solid #ccc;
 border-radius: 4px;
}

.todo-input button {
 padding: 10px 15px;
 background-color: #4caf50;
 color: white;
 border: none;
 border-radius: 4px;
 cursor: pointer;
 margin-left: 10px;
}

.todo-item {
 display: flex;
 align-items: center;
 padding: 10px;
 border-bottom: 1px solid #eee;
}

.todo-item input[type="checkbox"] {
 margin-right: 10px;
}

.todo-item span {
 flex-grow: 1;
}

.todo-item button {
 background-color: #f44336;
 color: white;
 border: none;
 border-radius: 4px;
 padding: 8px 12px;
 cursor: pointer;
}

.completed {
 text-decoration: line-through;
 color: #888;
}

Explanation:

  • We add basic styles for the body, container, headings, and buttons.
  • We style the input field and button in the ToDoInput component.
  • We style the individual to-do items, including the checkbox, text, and delete button.
  • We define a completed class to strikethrough completed tasks.

Testing Your Application

Now, save all the files and go back to your browser (http://localhost:3000). You should now see a functional to-do list app. You can:

  • Enter a task in the input field and click the “Add” button to add it to the list.
  • Click the checkbox to mark a task as complete.
  • Click the “Delete” button to remove a task.

Common Mistakes and How to Fix Them

Here are some common mistakes and how to avoid them:

  • Not Updating State Correctly: When updating the todos state, make sure you’re using the correct methods (setTodos) and immutably updating the array (e.g., using the spread operator ...). Incorrect state updates can lead to unexpected behavior and bugs.
  • Forgetting to Pass Props: Ensure that you pass all the necessary props to your components. For example, the ToDoItem component needs the todo object, onDeleteTodo, and onToggleComplete functions.
  • Incorrect CSS Selectors: Double-check your CSS selectors to ensure they’re targeting the correct elements. Use your browser’s developer tools to inspect the elements and see which styles are being applied.
  • ID Collisions: When generating IDs for your to-do items, make sure they are unique. Using Date.now() is a simple way to generate unique IDs in this case, but for more complex applications, consider using a library like uuid.

Key Takeaways

  • State Management is Essential: Understanding how to manage state with useState is fundamental to building interactive React applications.
  • Component Reusability: Breaking down your application into reusable components makes your code more organized and easier to maintain.
  • Event Handling: Handling user interactions (e.g., button clicks, form submissions) is a core part of building dynamic web apps.
  • CSS for Styling: Using CSS to style your components makes your application visually appealing and user-friendly.

SEO Best Practices

To ensure your to-do list app ranks well on search engines, consider these SEO best practices:

  • Descriptive Title and Meta Description: Use a clear and concise title (e.g., “Simple Next.js To-Do List App”) and a meta description that accurately summarizes your app’s purpose.
  • Keyword Optimization: Naturally incorporate relevant keywords (e.g., “Next.js”, “to-do list”, “React”) in your content, including headings and body text.
  • Clean URLs: Next.js automatically generates clean URLs.
  • Mobile-Friendly Design: Ensure your app is responsive and works well on all devices.
  • Fast Loading Speed: Optimize your images and code to ensure fast loading times. Next.js helps with this through features like image optimization and code splitting.

FAQ

1. How do I add persistence to my to-do list?

Currently, the to-do list data is stored in the browser’s memory and is lost when you refresh the page. To add persistence, you can use:

  • Local Storage: Store the todos array in the browser’s local storage. When the page loads, retrieve the data from local storage.
  • Server-Side Database: For more complex applications, use a database (e.g., MongoDB, PostgreSQL) to store the data on a server.

2. How can I add features like filtering and sorting?

To add filtering (e.g., show only completed tasks), you can add a filter option and update the todos array based on the selected filter. For sorting (e.g., sort by due date), you can use the sort() method on the todos array.

3. How can I deploy my Next.js app?

Next.js apps are easy to deploy. You can deploy to platforms like:

  • Vercel: The easiest way to deploy Next.js apps, as it’s built by the creators of Next.js.
  • Netlify: Another popular platform for deploying web apps.
  • Other Platforms: You can also deploy to platforms like AWS, Google Cloud, or Heroku.

4. How do I handle errors in my app?

Error handling is important for a good user experience. You can handle errors by:

  • Using try...catch blocks: Wrap any code that might throw an error in a try...catch block.
  • Displaying error messages: Show user-friendly error messages to the user.
  • Logging errors: Log errors to the console or a logging service for debugging.

5. Can I use a UI library like Material UI or Ant Design?

Yes, you can definitely use UI libraries like Material UI, Ant Design, or Chakra UI to speed up development and provide pre-built components. You’ll need to install the library and import the components into your project.

Building a to-do list app with Next.js is an excellent starting point for learning web development. This project provides a clear understanding of fundamental concepts such as state management, component composition, and event handling. By following the steps outlined in this tutorial, you’ve created a functional and interactive to-do list app. Remember to experiment with the code, try adding new features, and explore other Next.js features. With each project, you’ll gain more confidence and skill in building web applications, laying the groundwork for more ambitious and complex projects in the future. The ability to create functional applications from scratch is a powerful skill, and this simple to-do list is just the beginning of your journey into the world of web development.