Chapter 2: Build Core Todo
Todo List Component
Todo List Component
Let's extract the todo display into a proper React component. This is our first real component, and it sets the pattern for everything that follows.
The prompt
"Create a TodoList component at src/components/TodoList.tsx. It should accept an array of Todo items as props and render each one in a card-like list. Show the todo title, and if it's completed, show it with a line-through style. Use Tailwind CSS. Don't add any interactivity yet -- just display."
Notice the constraint: "Don't add any interactivity yet." Keeping prompts focused on one concern at a time produces cleaner code. We'll add checkboxes, buttons, and editing in later sections.
What Claude Code creates
'use client';
import { Todo } from '@/types/todo';
interface TodoListProps {
todos: Todo[];
}
export default function TodoList({ todos }: TodoListProps) {
return (
<div className="bg-white rounded-lg shadow-sm border border-gray-200 divide-y divide-gray-100">
{todos.map((todo) => (
<div
key={todo.id}
className="p-4 flex items-center gap-3"
>
<div className="flex-1">
<p
className={
todo.completed
? 'line-through text-gray-400'
: 'text-gray-900'
}
>
{todo.title}
</p>
</div>
</div>
))}
</div>
);
}Wire it into the page
"Update src/app/page.tsx to use the TodoList component instead of the inline list. Pass the sample todos as a prop."
Claude Code will update the page:
'use client';
import { useState } from 'react';
import { Todo } from '@/types/todo';
import { sampleTodos } from '@/data/sample-todos';
import TodoList from '@/components/TodoList';
export default function Home() {
const [todos, setTodos] = useState<Todo[]>(sampleTodos);
return (
<div className="space-y-4">
<TodoList todos={todos} />
</div>
);
}Notice that Claude Code introduced useState even though we only asked it to display todos. It's anticipating that we'll need to modify the list. This is the benefit of having a good CLAUDE.md -- Claude Code knows we're building a CRUD app.
Understanding the component pattern
Ask Claude Code to explain the design:
"Why did you use an interface for the props? And why is the component a default export?"
- Interface for props -- TypeScript interfaces make it clear exactly what data the component expects. Other developers (and Claude Code in future sessions) can immediately see the contract.
- Default export -- Next.js pages use default exports by convention, and it's cleaner when a file has one primary component. Named exports are better when a file exports multiple things.
- 'use client' directive -- This component will eventually have click handlers and state, which require client-side JavaScript.
Verify in the browser
Refresh http://localhost:3000. The page should look identical to before -- but now the code is properly organized with a reusable component.
This is a refactor, not a feature. The page looks the same, but the code is better structured. Good developers refactor early and often. Claude Code makes refactoring nearly free.
The pattern we're following
Every feature from here will follow this rhythm:
- Prompt Claude Code with a clear, scoped task
- Review what it creates
- Wire it in to the existing app
- Verify in the browser
- Ask questions to deepen understanding
This loop keeps you in control while moving fast.