Course navigation
Claude CodeLesson 9 of 25

Building Apps with Claude Code

End-to-end walkthrough: build 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.

LayerChoice
FrameworkNext.js 15 App Router + TypeScript
StylingTailwind CSS
DataIn-memory store — no database setup
TestsVitest — one test for the API route
localhost:3000

My Tasks

3 of 5 complete

Add a new task…
  • Set up project structure
  • Create API routes
  • Build task list UI
  • Add delete functionality
  • Write tests
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.

  1. Scaffold the project
  2. Write CLAUDE.md
  3. Build the API
  4. Build the UI
  5. Add delete functionality
  6. Write a test

Build steps

1

Scaffold the project

Create the project folder and let Claude set up the Next.js scaffold.

Terminal
$ mkdir my-tasks && cd my-tasks
$ claude
# Now inside Claude Code session
Y

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.

C

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.

Claude Code — scaffold output
Created my-tasks/
Installed dependencies (next, react, typescript, tailwindcss)
Removed example content from app/page.tsx
Run: npm run dev to start
Verify: run npm run dev and open localhost:3000. You should see a blank page before continuing.
2

Write CLAUDE.md

Before writing feature code, give Claude the project conventions once. Every future prompt in this project inherits them automatically.

Y

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__/.

C

I'll create CLAUDE.md at the root with those conventions now.

# CLAUDE.md
Stack: Next.js 15 App Router, TypeScript, Tailwind CSS
API routes: src/app/api/
Components: src/components/
Tests: Vitest, files in src/__tests__/
No console.log in production code
3

Build the API route

One API route handles all task operations. We use an in-memory array to keep things simple.

Y

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).

C

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.

Verify: open localhost:3000/api/tasks in your browser. You should see [].
4

Build the task UI

Two components: a form to add tasks and a list to display them.

Y

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.

C

I'll create TaskList.tsx as a client component with fetch calls for GET, PATCH, and DELETE.

Y

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 an onTaskAdded() prop so TaskList can re-fetch.

C

I'll create AddTaskForm.tsx with a controlled input, POST on submit, and an onTaskAdded callback prop.

Y

Update @src/app/page.tsx to render AddTaskForm and TaskList together. Centre them on the page with a heading 'My Tasks'.

C

I'll update page.tsx to import and render both components with a centred layout and the heading.

Verify: open localhost:3000, add a task, check it off. Both actions should update the list instantly.
5

Write a test

Ask Claude to write a test for the API route and verify it passes before finishing.

Y

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.

C

I'll create the test file and add Vitest to devDependencies. The tests will call the route handlers directly without a running server.

Terminal — running the test
$ npm run test
src/__tests__/tasks.test.ts
POST /api/tasks creates a task (12ms)
GET /api/tasks returns created task (4ms)
2 tests passed
If a test fails, paste the error back to Claude: “The test failed with this error: [paste]. Fix it.”

Where everything lives

my-tasks/
src/
app/api/tasks/route.ts
app/page.tsx
components/TaskList.tsx
components/AddTaskForm.tsx
__tests__/tasks.test.ts
CLAUDE.md
package.json

Quick reference

PracticeWhat we did
One feature per promptEach prompt asked for exactly one thing
@filename for precise editsUpdating page.tsx used @src/app/page.tsx
Verify after each stepChecked localhost before moving on
CLAUDE.md onceStack and conventions stated once, inherited by every prompt

Before you continue

  • Scaffold first, then write CLAUDE.md before any feature code.
  • One feature per prompt — API, then UI components, then page wiring, then tests.
  • Verify at localhost after each step so bugs do not compound.
  • Six prompts, five features, one working app with a passing test — that is the build loop.
  • Next: use Claude as your code reviewer.

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.