How to Build a Todo App with Next.js
- Authors
- Author
- Masaki Hayashi
- twitter @mh_poteto
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.
Shared tags: Next.js, React, TypeScript, ChatGPT
Building a Simple Bulletin Board App with Next.js and ChatGPT
A practical guide for building a lightweight bulletin board app with React, TypeScript, Next.js, and PostgreSQL.
Mar 18, 2023
Recent post
Build a Robust API Client in SwiftUI with async/await
A practical pattern for implementing API calls in SwiftUI with async/await, clear error handling, and testability.
Feb 20, 2026
Recent post
Hello World.
Hello World. A short introduction to this site and what I will share here.
Feb 23, 2023