Article · Jan 26, 2026

How I Used Claude Code to Build a Full-Stack React App: A Step-by-Step Development Guide

How I built a full-stack React + Next.js + TypeScript app with Claude Code: each step shows the prompt, what it produced, and what I corrected.

Software development gets mystified. Some call it magic, others call it chaos. The truth is a disciplined, iterative process where each step builds on the last.

I’m going to walk through exactly how I built React AI UI Generator using Claude Code, revealing every decision, every security consideration, and every correction along the way.

Note: The entire project is open source: React AI UI Generator on GitHub. Fork it, build on it, or use it as a reference for your own Claude Code projects.


Step 1: Ship something real before perfecting anything

My first commit wasn’t a planning document or architecture diagram. It was a working application.

I described what I wanted to build, Claude Code drafted the implementation, and I reviewed and corrected the diff. What came out of that first session:

  • A virtual file system (components stored in memory, not on disk, enabling instant operations)
  • Real-time preview with live code transformation
  • Code generation using the Vercel AI SDK (the app writes React components for you)
  • User authentication and project persistence
  • Prisma ORM with SQLite for structured storage

Why not plan first?

A working prototype reveals problems no design document can predict. Edge cases, UX friction, and technical constraints surface within days rather than months. And stakeholders see progress, not promises.

The risk of the planning-first approach isn’t strategic, it’s psychological: the longer you spend in a doc, the more committed you feel to assumptions you haven’t tested yet.


Step 2: Security is the foundation, not a phase

Less than 24 hours after my initial commit, the second major update focused entirely on hardening the application. Not new features.

What I implemented

Rate limiting (controlling request frequency to prevent abuse):

  • 3 sign-up attempts per hour
  • 5 login attempts per 15 minutes
  • 10 API requests per hour for anonymous users

Input validation at every entry point:

  • Email format verification
  • Password strength requirements (8+ characters, mixed case, numbers)
  • Path traversal prevention (blocking attempts to access unauthorized files)

Graceful degradation:

Graceful degradation: the application continues to function in a reduced state when a dependency is unavailable or a configuration is incomplete. Here, users without API keys get a demo mode with limited functionality rather than a broken screen.

File system limits:

  • 100 files per project maximum
  • 500KB per file, 5MB total
  • Whitelist of allowed extensions (.js, .jsx, .css, and a handful of others)

Security vulnerabilities discovered after launch cost exponentially more to fix. A breach in week one destroys user trust before it has formed.


Step 3: Document why, not what

Most documentation describes what exists. The kind that actually helps describes why things exist and how they connect.

I created a CLAUDE.md file: a living reference document that gives Claude Code (and any developer) the context needed to make decisions aligned with the project’s architecture.

The documentation evolved across three versions over two days:

Version 1 (day 1): Basic commands, high-level architecture, data flow diagrams in plain text, database schema.

Version 2 (day 2): Security features, demo mode explanation, rate limiting specifics.

Version 3 (days 2-3): Multi-provider architecture, encryption methods for user API keys, full API endpoint reference.

Here is the concrete difference between documentation that helps and documentation that doesn’t:

Bad: “VirtualFileSystem class in file-system.ts”

Good: “The unique architecture centers on a virtual file system. Components live in memory, not on disk. This enables instant file operations without I/O delays, live preview updates, user experimentation without filesystem changes, and atomic save operations to the database.”

The second version tells you intent. New team members understand philosophy. Claude Code makes aligned suggestions. Future maintainers don’t accidentally break architectural assumptions.


Step 4: Extend rather than rewrite

The third day brought a major expansion: multi-provider support.

I went from one AI provider (Anthropic) to five:

  • Anthropic Claude (Sonnet, Haiku, Opus)
  • OpenAI (GPT-4o, GPT-4o Mini, GPT-4 Turbo)
  • Google AI (Gemini 2.0 Flash, Gemini 1.5 Pro/Flash)
  • OpenRouter (multi-provider access)
  • xAI (Grok 2, Grok 2 Mini)

This wasn’t a rewrite. The extension strategy had two parts.

First, I introduced a provider registry before adding any providers.

Provider registry: a central configuration object that defines each provider’s name, available models, and settings. Adding a sixth provider means adding one entry to the registry, not modifying existing provider code.

Second, users can bring their own API keys. These are stored with AES-256-GCM encryption (a symmetric authenticated cipher). The encryption key derives from the JWT secret using scrypt (a memory-hard key derivation function that makes brute-force expensive). If someone compromises the database, the API keys are still unreadable.

The fallback chain: environment variable, then user key, then demo mode. The application always works even when configurations are incomplete.


Step 5: The user experience layer

Technical correctness doesn’t carry you to a usable product.

Several UX improvements landed alongside the technical work:

Human-readable status messages. Technical tool names like str_replace_editor became “Creating App.jsx”. Small change, significant difference in how the application feels to a non-developer.

Theme support. Dark and light modes.

Project management. Rename, delete, organize. Features that seem obvious but mark the line between a prototype and a real tool.

Thoughtful empty states. A centered, welcoming interface instead of a blank void when starting fresh.


Step 6: Test for confident change, not for the count

By project completion: 252 tests across multiple test files. But the count matters less than the coverage strategy.

  • Unit tests for core utilities: virtual file system, JSX transformation
  • Integration tests for contexts that coordinate multiple systems
  • Component tests for user-facing behavior

Each test file lives alongside the code it tests (in __tests__ directories). When multi-provider support was added, the existing tests caught regressions in the original single-provider behavior immediately.

Tests don’t exist to catch bugs. They exist to make change safe.


The stack

LayerTechnology
FrontendReact 19, Next.js 15 (App Router), TypeScript
StylingTailwind CSS v4, Radix UI, next-themes
AI providersAnthropic Claude, OpenAI GPT-4o, Google Gemini, OpenRouter, xAI Grok
AI integrationVercel AI SDK (unified interface for all providers)
DatabasePrisma ORM with SQLite
SecurityAES-256-GCM encryption, scrypt key derivation, JWT auth
Code editorMonaco Editor with Babel standalone (JSX transform)
TestingVitest, React Testing Library

The Vercel AI SDK was the critical choice here. It provides a unified interface for multiple AI providers, which made adding new providers trivial rather than a major refactor. Without it, multi-provider support would have been a rewrite, not an extension.


What Claude Code handled and where I took over

Claude Code excelled at: boilerplate, security pattern implementation, test scaffolding, maintaining consistency across files when the pattern was established.

I handled: every architectural decision (data model, auth flow, provider abstraction), security priorities, UX calls, and when to stop adding features.

The architectural decisions are the expensive ones. Get those wrong and no amount of correct boilerplate saves you.


Start building

The entire project is open source: github.com/nocodeanish/react-ui-generator.

If you’re working on a full-stack build and want a second set of eyes on the architecture before you start generating code, I’m available for a focused consultation.

Frequently asked questions

What does building production features in Claude Code actually look like?

A loop: you write a short spec or user story, Claude Code drafts the implementation across multiple files, you review the diff, you correct the parts that drifted from intent, then Claude Code refines. The goal is not zero-touch generation; it is reducing the keystroke cost so you spend your time on architecture and correctness, not boilerplate.

What stack pairs well with Claude Code for a new full-stack app?

TypeScript everywhere (Claude Code reasons better with types in scope), Next.js or Astro for the framework (large training-data presence), Tailwind for styling (deterministic class names the tool handles reliably), and a typed ORM like Prisma or Drizzle for the data layer. Reduce surface area for Claude Code to invent inconsistent patterns.

When should I let Claude Code generate versus write code by hand?

Generate for boilerplate, schema migrations, test scaffolds, repetitive UI components, and any place where the pattern is well-established in the codebase. Write by hand when the decision space is open: auth flow design, data model, third-party integration design. Claude Code will pick a plausible-looking path that is hard to back out of later.

How do I keep Claude Code from inventing wrong code in a full-stack project?

Two guardrails do most of the work: a CLAUDE.md at the repo root covering commands, architecture, and what not to touch; and a typed codebase so the LSP catches drift before the diff is accepted. A test suite that runs on every save catches the rest.