How to Build a Todo App with Next.js

Published: Friday, March 17, 2023 Tags: 4
Authors

This article explains how to build a simple Todo app with React, TypeScript, and Next.js.

Introduction

A Todo app is a great starter project for learning UI composition and state handling. In this guide, we focus on a small but practical implementation.

Project Setup

Create a new Next.js project and install dependencies.

npx create-next-app todo-app
cd todo-app
npm install react react-dom next typescript

Build UI Components

Create the following files:

  • components
    • TodoItem.tsx
    • TodoList.tsx
    • TodoForm.tsx
  • pages
    • index.tsx

TodoItem renders one item, TodoList renders a list, and TodoForm handles new input.

TodoItem

import React from "react";

type TodoItemProps = {
  id: string;
  text: string;
  completed: boolean;
  onComplete: (id: string) => void;
  onDelete: (id: string) => void;
};

export const TodoItem: React.FC<TodoItemProps> = ({
  id,
  text,
  completed,
  onComplete,
  onDelete,
}) => {
  const handleComplete = () => {
    onComplete(id);
  };

  const handleDelete = () => {
    onDelete(id);
  };

  return (
    <div>
      <input type="checkbox" checked={completed} onChange={handleComplete} />
      <span>{text}</span>
      <button onClick={handleDelete}>Delete</button>
    </div>
  );
};

TodoList

import React from "react";
import { TodoItem } from "./TodoItem";

type TodoListProps = {
  items: {
    id: string;
    text: string;
    completed: boolean;
  }[];
  onComplete: (id: string) => void;
  onDelete: (id: string) => void;
};

export const TodoList: React.FC<TodoListProps> = ({
  items,
  onComplete,
  onDelete,
}) => {
  return (
    <div>
      {items.map((item) => (
        <TodoItem
          key={item.id}
          id={item.id}
          text={item.text}
          completed={item.completed}
          onComplete={onComplete}
          onDelete={onDelete}
        />
      ))}
    </div>
  );
};

TodoForm

import React, { useState } from "react";

type TodoFormProps = {
  onSubmit: (text: string) => void;
};

export const TodoForm: React.FC<TodoFormProps> = ({ onSubmit }) => {
  const [text, setText] = useState("");

  const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    onSubmit(text);
    setText("");
  };

  const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    setText(event.target.value);
  };

  return (
    <form onSubmit={handleSubmit}>
      <input type="text" value={text} onChange={handleChange} />
      <button type="submit">Add</button>
    </form>
  );
};

Data Model and State

type TodoItem = {
  id: string;
  text: string;
  completed: boolean;
};

type TodoList = TodoItem[];

Manage the list in pages/index.tsx with useState.

import React, { useState } from "react";
import { TodoList } from "../components/TodoList";
import { TodoForm } from "../components/TodoForm";

type TodoItem = {
  id: string;
  text: string;
  completed: boolean;
};

type TodoList = TodoItem[];

const Home: React.FC = () => {
  const [todos, setTodos] = useState<TodoList>([]);

  const handleAddTodo = (text: string) => {
    const newTodo: TodoItem = {
      id: Math.random().toString(36).substring(7),
      text,
      completed: false,
    };
    setTodos([...todos, newTodo]);
  };

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

  const handleDeleteTodo = (id: string) => {
    setTodos(todos.filter((todo) => todo.id !== id));
  };

  return (
    <div>
      <h1>Todo App</h1>
      <TodoList items={todos} onComplete={handleCompleteTodo} onDelete={handleDeleteTodo} />
      <TodoForm onSubmit={handleAddTodo} />
    </div>
  );
};

export default Home;

Wrap-up

You now have a complete Todo app with typed components and simple state management. From here, useful next steps are persistence, filtering, and drag-and-drop sorting.

Related Articles

Ranked by shared tags and recency of publication date.

Recent post

Hello World.

Hello World. A short introduction to this site and what I will share here.

Feb 23, 2023