Skip to content

How to show the unfinished to-do count in the page title

This example demonstrates how to show the unfinished to-do count in the page title using the useTitle hook. It includes code in both TypeScript and JavaScript versions.

Watch the live preview.

tsx
import {useEffect, useState} from "react";
import {useTitle} from "./useTitle";

interface Task {
    id: number;
    title: string;
    isDone: boolean;
}

const appTasks: Task[] = [
    {id: 1, title: "Task 1", isDone: false},
    {id: 2, title: "Task 2", isDone: false},
    {id: 3, title: "Task 3", isDone: false},
    {id: 4, title: "Task 4", isDone: false},
    {id: 5, title: "Task 5", isDone: false},
];

export default function App() {
    const [tasks, setTasks] = useState<Task[]>(appTasks);
    const {changeTitle} = useTitle();

    useEffect(() => {
        const unfinishedTasks = tasks.filter((task) => !task.isDone).length;
        changeTitle(`${unfinishedTasks} pending tasks`);
    }, []);

    // Handle check/uncheck task
    const checkTask = (id: number, isDone: boolean) => {
        const updatedTasks = tasks.map((task) =>
            task.id === id ? {...task, isDone} : task
        );
        setTasks(updatedTasks);

        // Set the title depending on the number of tasks left
        const unfinishedTasks = updatedTasks.filter((task) => !task.isDone).length;
        const newTitle =
            unfinishedTasks !== 1
                ? `${unfinishedTasks} pending tasks`
                : `${unfinishedTasks} pending task`;
        changeTitle(newTitle);
    };

    return (
        <>
            <h1>Tasks</h1>
            <ul>
                {tasks.map((task) => (
                    <li key={task.id}>
                        <input
                            type="checkbox"
                            checked={task.isDone}
                            onChange={(e) => checkTask(task.id, e.target.checked)}
                        />
                        {task.title}
                    </li>
                ))}
            </ul>
        </>
    );
}
typescript
import {useState} from "react";

export const useTitle = () => {
    const [title, setTitle] = useState<string>(document.title);

    const changeTitle = (newTitle: string) => {
        document.title = newTitle;
        setTitle(newTitle);
    };

    return {title, changeTitle};
};