Chapter 2: Build Core Todo
Due Dates
Due Dates
Deadlines keep us accountable. Let's add due date support with a date picker, formatted display, and visual indicators for overdue items that demand attention.
Update the data model
"Add an optional dueDate field (string or null) to the Todo interface in src/types/todo.ts. Use string format so it serializes cleanly to localStorage."
export interface Todo {
id: string;
title: string;
completed: boolean;
createdAt: string;
category: Category;
dueDate: string | null;
}The dueDate is string | null rather than Date because we store dates as ISO strings. This makes serialization to localStorage straightforward later. We parse to Date objects only when we need to compare or display.
Update the AddTodo form
"Add an optional date input to the AddTodo form. Place it between the category select and the Add button. Use a native HTML date input. Set the min attribute to today's date so users cannot pick past dates. The onAdd callback should now include the dueDate (string or null)."
Claude Code will add the date input:
const [dueDate, setDueDate] = useState<string>('');
// In the form:
<input
type="date"
value={dueDate}
onChange={(e) => setDueDate(e.target.value)}
min={new Date().toISOString().split('T')[0]}
className="px-3 py-2 border border-gray-300 rounded-lg
focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
// In handleSubmit:
onAdd(trimmed, category, dueDate || null);
setDueDate('');The min attribute on the date input prevents users from selecting past dates for new todos. Claude Code adds this kind of validation automatically when it understands you are building a due date feature. Small constraints like this prevent bad data at the source.
Display due dates in the list
"Update the TodoList component to display the due date below each todo's title. If the due date is today, show it in orange with 'Due today'. If it's in the past and the todo is not completed, show it in red with 'Overdue'. Otherwise show the formatted date in gray. Use toLocaleDateString for formatting."
Claude Code will add a due date display function:
const formatDueDate = (dueDate: string | null, completed: boolean) => {
if (!dueDate) return null;
const today = new Date();
today.setHours(0, 0, 0, 0);
const due = new Date(dueDate + 'T00:00:00');
const isToday = due.getTime() === today.getTime();
const isOverdue = due < today && !completed;
let className = 'text-xs mt-1 ';
let text = '';
if (isOverdue) {
className += 'text-red-500 font-medium';
text = `Overdue: ${due.toLocaleDateString()}`;
} else if (isToday) {
className += 'text-orange-500 font-medium';
text = 'Due today';
} else {
className += 'text-gray-400';
text = `Due: ${due.toLocaleDateString()}`;
}
return <p className={className}>{text}</p>;
};And in the todo item JSX, right after the title:
{formatDueDate(todo.dueDate, todo.completed)}Update page.tsx
"Update the addTodo function in page.tsx to accept and store the dueDate. Update sample todos to include some due dates for testing."
const addTodo = (title: string, category: Category, dueDate: string | null) => {
const newTodo: Todo = {
id: generateId(),
title,
completed: false,
createdAt: new Date().toISOString(),
category,
dueDate,
};
setTodos([newTodo, ...todos]);
};Asking about edge cases
Here's a prompting technique that prevents bugs: ask Claude Code about edge cases before you ship.
"What happens if the user selects a date in the past? What about today's date? What edge cases should we handle for due dates?"
Claude Code will identify scenarios you might not have thought of — timezone issues, midnight boundaries, what "overdue" means at exactly midnight. This edge case prompting habit catches bugs that testing alone might miss.
Make this a habit: after Claude Code builds any feature, ask "What edge cases should we handle?" It's like getting a free code review from a detail-oriented teammate.
Date handling is notoriously tricky. The + 'T00:00:00' suffix when parsing ensures we compare dates without timezone offset issues. If you see dates off by one day, ask Claude Code: "I'm seeing timezone issues with due dates. Can you fix the date parsing?" It will know exactly what to adjust.
Why native date inputs?
You might wonder why we use <input type="date"> instead of a date picker library. Ask Claude Code:
"Should we use a date picker library like react-datepicker instead of the native date input?"
For a learning project, the native input is perfect: zero dependencies, works everywhere, and looks decent on modern browsers. For a production app, a library gives you more control over styling and behavior.
Test due dates
- Add a todo with today's date -- it should show "Due today" in orange
- Add a todo with a future date -- it should show "Due: [date]" in gray
- Add a todo with no date -- no due date should display
- Manually edit a sample todo to have a past date -- it should show "Overdue" in red
- Mark an overdue todo as completed -- the red "Overdue" styling should disappear
Todos now have deadlines. Next: adding priority levels to help users focus on what is important.