Building Apps with Claude Code
The best way to learn Claude Code is to build something real. This lesson walks you through building a task manager app from a blank folder to a working app with an API, a React UI, and a passing test — using nothing but Claude Code prompts.
What We're Building
A simple task manager: add tasks, check them off, delete them. Small enough to finish in one session, real enough to cover the full build flow.
Next.js 15
App Router + TypeScript
In-memory store
No database — keep it simple
Vitest
One test for the API route
My Tasks
3 of 5 complete
- Set up project structure
- Create API routes
- Build task list UI
- Add delete functionality
- Write tests
Built with Claude Code - Next.js + TypeScript
The finished app — built entirely by describing features to Claude Code.
The Approach
We follow the habits from the previous lesson: one feature at a time, specific prompts, @-mention files, verify before continuing.
Scaffold the Project
Create the project folder and let Claude set up the Next.js scaffold. Open a terminal and start Claude Code.
Scaffold a new Next.js 15 app with TypeScript and Tailwind. Use the App Router. Call it my-tasks. Do not add any example content — just the clean starter files.
I'll scaffold a clean Next.js 15 app with TypeScript and Tailwind using the App Router. Running npx create-next-app@latest with those options now⬦
npm run dev and open localhost:3000. You should see a blank page. Only continue once it loads without errors.Write CLAUDE.md
Before writing any feature code, give Claude the project conventions once. This way every future prompt in this project gets the right context automatically.
Create a CLAUDE.md at the project root. Document: stack is Next.js 15 App Router + TypeScript + Tailwind. API routes go in src/app/api/. Components go in src/components/. Use 'use client' only when needed. No console.log in production code. Tests use Vitest, colocated in src/__tests__/.
I'll create CLAUDE.md at the root with those conventions now.
Build the API Route
One API route handles all task operations. We use an in-memory array to keep things simple — no database setup.
Create src/app/api/tasks/route.ts. It should export GET (return all tasks), POST (add a task with id and text), PATCH (toggle done by id), and DELETE (remove by id). Use an in-memory array. Each task has: id (number), text (string), done (boolean).
I'll create the API route at src/app/api/tasks/route.ts with GET, POST, PATCH, and DELETE handlers using an in-memory array. No database needed for this.
localhost:3000/api/tasks in your browser. You should see []. If you do, the API is working.Build the Task UI
Two components: a form to add tasks and a list to display them. We tell Claude exactly what each component should do.
Create src/components/TaskList.tsx — a 'use client' component. It fetches tasks from /api/tasks on load and re-fetches after any change. Each task shows a checkbox (toggles done via PATCH), the task text (strikethrough when done), and a delete button (DELETE). Style with Tailwind — clean, minimal.
I'll create TaskList.tsx as a client component with fetch calls for GET, PATCH, and DELETE. Tailwind classes for a clean minimal style.
Create src/components/AddTaskForm.tsx — a 'use client' component with a text input and an Add button. On submit, POST to /api/tasks with the input value, clear the field, then call a onTaskAdded() prop so TaskList can re-fetch.
I'll create AddTaskForm.tsx with a controlled input, POST on submit, and an onTaskAdded callback prop.
Update @src/app/page.tsx to render AddTaskForm and TaskList together. Centre them on the page with a heading 'My Tasks'.
I'll update page.tsx to import and render both components with a centred layout and the heading.
localhost:3000, add a task, check it off. If both actions update the list instantly, this step is complete.Where Everything Lives
After the scaffold, CLAUDE.md, API route, and two components, your project structure looks like this.
Blue = Next.js App Router files - Green = React components - Yellow = test file - Purple = CLAUDE.md
Write a Test
Ask Claude to write a test for the API route — happy path for POST, then verify it actually passes before finishing.
Write a Vitest test in src/__tests__/tasks.test.ts for the /api/tasks route. Test that POST creates a task and that GET returns it. Install Vitest if not already in package.json.
I'll create the test file and add Vitest to devDependencies. The tests will call the route handlers directly without a running server.
What Just Happened
One feature per prompt
Each prompt asked for exactly one thing. Claude never had to guess scope.
@filename kept edits precise
When updating page.tsx we used @src/app/page.tsx — Claude touched only that file.
Verified after each step
We checked localhost before moving on. No broken foundations to debug later.
CLAUDE.md did the heavy lifting
Stack and conventions were stated once. Every prompt inherited them automatically.
6 prompts - 5 features - a working app with a passing test
This is the Claude Code build loop.
What's Next
You've built a real app end-to-end with Claude Code. The next lesson shows how to use Claude as your code reviewer — catching issues before they ship.