Back to blog
·7 min read

How to Structure a Large Next.js Project Without Chaos: A Production Guide

Most Next.js projects start clean but become unmaintainable within months. This guide shows you exactly where to put server actions, client components, and business logic so your codebase scales without falling apart.

The Problem Every Next.js Project Faces

You start a Next.js project with confidence. The app/ directory feels clean. You add a few routes, some components, maybe a utils/ folder. Things work great.

Then your project gets real.

Suddenly you have 47 components living directly in app/. You have server actions scattered across six different files. Business logic lives in utils/, lib/, and inside components. Someone puts an API route in the wrong place. A new team member asks where to put authentication helpers, and the answer is "uh... somewhere?"

This isn't a Next.js problem. It's a human problem. Without clear structure, intelligent developers with good intentions create chaos.

The difference between a codebase that scales and one that collapses in six months isn't the framework. It's the folder structure.

Why Default Next.js Structure Breaks at Scale

The app/ directory is designed for routes and layouts. It's not designed to hold your entire application. Yet most developers treat it exactly like that.

Here's what happens:

When you follow the default Next.js pattern, everything lives at the same level. Routes live next to components live next to hooks live next to utilities. As your project grows to 50+ routes and hundreds of components, finding anything becomes a scavenger hunt.

The problem gets worse when multiple developers work on the same codebase. Without explicit structure, everyone has a different mental model of where things belong. Code gets duplicated. Refactoring becomes terrifying because you're not sure what depends on what.

Most critically: AI coding assistants like Claude Code struggle with poorly structured projects. When your codebase has no clear patterns, Claude Code spends tokens understanding your structure instead of building features.

The Battle-Tested Structure That Actually Scales

Here's the structure that works for production SaaS applications:

```

project-root/

├── app/ # Next.js app directory - routes only

│ ├── (auth)/

│ │ ├── login/

│ │ ├── signup/

│ │ └── layout.tsx

│ ├── (dashboard)/

│ │ ├── layout.tsx

│ │ └── [workspace]/

│ ├── api/ # API routes only

│ │ ├── auth/

│ │ ├── webhooks/

│ │ └── ai/

│ └── layout.tsx

├── src/

│ ├── components/ # Reusable UI components

│ │ ├── ui/ # Base components (Button, Card, etc)

│ │ ├── forms/ # Form components

│ │ └── layout/ # Layout components

│ │

│ ├── features/ # Feature-specific code

│ │ ├── auth/

│ │ │ ├── hooks/

│ │ │ ├── actions/

│ │ │ ├── types.ts

│ │ │ └── utils.ts

│ │ ├── workspace/

│ │ ├── billing/

│ │ └── projects/

│ │

│ ├── lib/ # Shared utilities and helpers

│ │ ├── db.ts # Database client

│ │ ├── auth.ts # Auth utilities

│ │ ├── validators.ts # Zod schemas

│ │ └── utils.ts # Generic helpers

│ │

│ ├── actions/ # Server actions

│ │ ├── auth.ts

│ │ ├── workspace.ts

│ │ └── projects.ts

│ │

│ ├── hooks/ # Custom React hooks

│ │ └── index.ts

│ │

│ ├── types/ # TypeScript types

│ │ └── index.ts

│ │

│ └── config/ # Configuration

│ └── constants.ts

└── public/

```

This structure solves three critical problems:

Problem 1: Where Does Business Logic Live?

In poorly structured projects, business logic gets scattered everywhere. It lives in components, in utilities, in server actions, even in hooks.

In this structure: Business logic lives in the features/ directory, organized by feature. All workspace-related logic lives in src/features/workspace/. All billing logic lives in src/features/billing/.

Within each feature, you have:

  • actions/ for server actions
  • hooks/ for custom hooks
  • utils.ts for helper functions
  • types.ts for TypeScript types
  • This makes it trivial to find and modify business logic.

    Problem 2: Client vs Server Code Confusion

    Developers struggle with Next.js's server-first approach. They're not sure which code runs on the server and which runs on the client.

    In this structure, the convention is clear:

  • api/ routes are always server-side
  • actions/ are always server actions
  • components/ can be client or server (use "use client" directive)
  • lib/ utilities used by actions are server-side
  • hooks/ are always client-side (they use React hooks)
  • By naming your directories with their execution context in mind, developers immediately understand what they're looking at.

    Problem 3: Scaling With Multiple Teams

    When your team grows beyond five developers, different people own different features. The workspace team shouldn't need to understand how billing works.

    By organizing into features, teams own their own directories. The workspace team works in src/features/workspace/. The billing team works in src/features/billing/. They share utilities from lib/, but otherwise stay separate.

    This prevents merge conflicts and makes code reviews faster because changes are isolated by feature.

    How AI Coding Assistants Work Better With Structure

    Here's why this matters for Claude Code and similar tools:

    When Claude Code analyzes a codebase, it needs to understand relationships between files. In a messy structure, those relationships are unclear. Claude spends tokens figuring out what depends on what.

    In a structured codebase, patterns are obvious. Claude immediately understands that all workspace logic lives in one place. It knows server actions live in actions/. It knows utilities live in lib/. This means Claude spends tokens actually building features instead of deciphering your architecture.

    When you use tools designed for AI agent productivity, structure becomes force multiplier. The clearer your patterns, the better Claude Code performs.

    Specific Rules for This Structure

    Follow these rules and your codebase stays maintainable:

  • app/ contains only routes, layouts, and API handlers. No components live here.
  • components/ contains only UI components. No business logic.
  • features/ contains all domain-specific code. If you're unsure where something belongs, ask: "Is this related to a specific feature?" If yes, put it in features/.
  • lib/ contains only truly shared utilities. If something is used by only one feature, put it in that feature's directory instead.
  • Server actions live in src/actions/ or src/features/[feature]/actions/. Never bury them in component files.
  • Custom hooks live in src/hooks/ or src/features/[feature]/hooks/. Never create hooks inside components.
  • Types live in src/types/ or src/features/[feature]/types.ts. Use barrel exports (export * from './types') to share them.
  • Implementation: Moving Existing Projects

    If you're refactoring an existing project:

    Start with the lib/ directory. Move generic utilities there first. Then establish a features/ directory and move feature-specific code into it. Finally, clean up app/ to remove any components that snuck in there.

    This takes a few hours for a medium project, and it pays dividends immediately in developer experience and maintainability.

    When Scaffolding Saves Time

    Building this structure manually is straightforward but tedious. This is where AI-powered scaffolding tools like ZipBuild help. Rather than manually creating 15 directories and example files, you define your features and domain entities once, and the scaffold generates the entire structure with proper separation of concerns and TypeScript types already configured.

    This removes the "how do I start?" friction and lets teams jump straight to building features.

    Next Steps

    Print this structure. Use it as your template for the next three projects. Within a month, it becomes muscle memory. Within six months, you'll wonder how you ever worked without it.

    Better structure means faster development, easier collaboration, and happier developers.

    Try the free discovery chat at zipbuild.dev to see how proper scaffolding can eliminate this setup work entirely.

    Written by ZipBuild Team

    Ready to build with structure?

    Try the free discovery chat and see how ZipBuild architects your idea.

    Start Building