DEV Community

Cover image for How I Structure Large-Scale Next.js Applications in 2026
Vrushik Visavadiya
Vrushik Visavadiya

Posted on

How I Structure Large-Scale Next.js Applications in 2026

When a Next.js project is small, almost any folder structure works.

But as the application grows, things start getting messy:

  • Where should this component live?
  • Should this hook be global or feature-specific?
  • Where should API logic go?
  • Where should business logic live?
  • Should this page be a Server Component or Client Component?
  • When should I create a new folder?

These decisions may seem small at first, but they have a big impact on maintainability as the project grows.

In this article, I'll share a practical folder structure I use for medium-to-large Next.js applications using the App Router.

The main idea is simple:

Keep routes focused on routing, features focused on business logic, and shared folders truly shared.

The structure

my-next-app/
├── public/
│
├── src/
│   ├── app/
│   │   ├── (auth)/
│   │   │   ├── login/
│   │   │   └── register/
│   │   │
│   │   ├── (dashboard)/
│   │   │   ├── dashboard/
│   │   │   ├── projects/
│   │   │   └── settings/
│   │   │
│   │   ├── api/
│   │   │   └── projects/
│   │   │       └── route.ts
│   │   │
│   │   ├── layout.tsx
│   │   ├── loading.tsx
│   │   ├── error.tsx
│   │   └── not-found.tsx
│   │
│   ├── features/
│   │   ├── auth/
│   │   ├── projects/
│   │   ├── users/
│   │   └── billing/
│   │
│   ├── components/
│   │   ├── ui/
│   │   ├── layout/
│   │   └── shared/
│   │
│   ├── lib/
│   │   ├── db/
│   │   ├── auth/
│   │   ├── api/
│   │   ├── validation/
│   │   └── logger/
│   │
│   ├── hooks/
│   ├── config/
│   ├── types/
│   └── styles/
│
├── next.config.ts
├── package.json
└── tsconfig.json
Enter fullscreen mode Exit fullscreen mode

Why feature-based architecture?

Instead of organizing everything by technical type:

components/
hooks/
services/
schemas/
Enter fullscreen mode Exit fullscreen mode

I prefer organizing application-specific code by feature:

features/
├── auth/
├── projects/
├── users/
└── billing/
Enter fullscreen mode Exit fullscreen mode

For example:

features/projects/
├── components/
├── hooks/
├── services/
├── schemas/
├── types.ts
└── index.ts
Enter fullscreen mode Exit fullscreen mode

Now, when I need to work on the projects feature, most of the relevant code is in one predictable place.

This also makes ownership much clearer.

Keep app/ focused on routing

One of the mistakes I see in growing Next.js applications is putting too much business logic inside app/.

I prefer keeping the App Router responsible mainly for:

  • Routes
  • Layouts
  • Loading states
  • Error states
  • Route handlers
  • Page composition

For example:

export default async function ProjectsPage() {
  const projects = await getProjects();

  return <ProjectsView projects={projects} />;
}
Enter fullscreen mode Exit fullscreen mode

The route defines where the page exists.

The feature defines what the page does.

Keep shared components actually shared

Your global components/ directory should not become a dumping ground.

Good candidates:

components/
├── ui/
│   ├── button.tsx
│   ├── input.tsx
│   └── dialog.tsx
│
├── layout/
│   ├── header.tsx
│   ├── sidebar.tsx
│   └── footer.tsx
│
└── shared/
    ├── empty-state.tsx
    ├── loading-state.tsx
    └── error-state.tsx
Enter fullscreen mode Exit fullscreen mode

If only one feature uses a component, I generally keep it inside that feature.

features/projects/components/project-card.tsx
Enter fullscreen mode Exit fullscreen mode

instead of:

components/project-card.tsx
Enter fullscreen mode Exit fullscreen mode

This simple rule can prevent your global components folder from becoming difficult to maintain.

Server Components by default

With the App Router, I generally start with Server Components.

A component should become a Client Component when it actually needs things such as:

  • useState
  • useEffect
  • Browser APIs
  • Event handlers
  • Client-only libraries
  • Interactive UI

And even then, I try to keep the client boundary as small as possible.

For example:

Dashboard
├── Server Component
│   ├── Data fetching
│   └── Statistics
│
└── Client Component
    └── InteractiveChart
Enter fullscreen mode Exit fullscreen mode

You don't necessarily need to turn the entire page into a Client Component just because one small part needs interactivity.

Where should API logic live?

I like separating HTTP concerns from business logic.

For example:

app/api/projects/route.ts
        ↓
features/projects/services/create-project.ts
        ↓
lib/db/
Enter fullscreen mode Exit fullscreen mode

The route handler deals with the HTTP layer.

The feature service handles the business logic.

The infrastructure layer handles things such as database access.

This separation makes the application easier to test and change later.

Don't create a giant utils/ folder

We've all seen this:

utils/
├── helper.ts
├── common.ts
├── format.ts
├── misc.ts
└── something.ts
Enter fullscreen mode Exit fullscreen mode

The problem isn't the folder itself.

The problem is that ownership becomes unclear.

Instead, ask:

Who owns this code?

If it's feature-specific:

features/projects/
Enter fullscreen mode Exit fullscreen mode

If it's infrastructure:

lib/
Enter fullscreen mode Exit fullscreen mode

If it's genuinely reusable UI:

components/
Enter fullscreen mode Exit fullscreen mode

If it's a globally reusable hook:

hooks/
Enter fullscreen mode Exit fullscreen mode

Good architecture isn't about having more folders.

It's about making ownership obvious.

Final thoughts

There isn't one perfect Next.js folder structure.

A small application doesn't need an enterprise architecture.

But once your application starts growing, having clear boundaries becomes extremely valuable.

The principles I try to follow are:

  1. Keep app/ focused on routing.
  2. Organize business functionality by feature.
  3. Keep shared components genuinely shared.
  4. Prefer Server Components by default.
  5. Keep Client Components small.
  6. Separate API/HTTP concerns from business logic.
  7. Keep feature-specific schemas and types close to the feature.
  8. Avoid giant utils/, hooks/, and types/ folders.
  9. Optimize for discoverability and ownership.
  10. Don't over-engineer before you actually need the structure.

The goal isn't to create the most complicated architecture.

The goal is to create an architecture where the next developer can quickly answer: "Where does this code belong?"


I've written a more detailed version with the complete folder structure and explanations here:

👉 https://www.vrushikvisavadiya.com/blog/best-nextjs-folder-structure-for-large-scale-applications

If you have a different approach to structuring large Next.js applications, I'd love to hear it.

How do you structure your Next.js projects?

Top comments (3)

Collapse
 
svgicons profile image
Svg/icons

How do you handle icons in that structure? I’d keep the raw SVG primitive shared, but feature-specific icon semantics close to the feature that owns them.

Collapse
 
vrushikvisavadiya profile image
Vrushik Visavadiya

Yes, definitely. For Next.js, I prefer keeping reusable UI icons in src/components/icons, while public/ is better for static assets that need to be accessed by URL.

Some comments may only be visible to logged-in visitors. Sign in to view all comments.