Scalable folder structure for Next.js projects
Published on October 5, 2025 by Tomas Mikula
Next.js and React offer great capabilities and flexibility, but with such freedom comes lot of question marks.
One of them is the most underrated productivity boosts in a growing Next.js project - a clear and scalable folder structure. A good structure not only helps new developers onboard faster, but also makes refactoring and scaling easier.
So let's get straight to it. Here is a pattern AND principles I have found effective when working with Next.js App Router projects (including localization).
Remember: the principles (described later) are more important than folders themselves.
├─ app
│ └─🏠[locale] # Routes and layouts
│ │ ├─ pageA
│ │ └─ pageB
│ │
│ └─ 🔗api # API routes & webhooks
│
├─ 📦components
│ ├─ layouts # Wrapper layouts
│ ├─ shared # App wide layout (nav, footer)
│ └─ ui # Smallest UI primitives
│
├─ ⚙️config # Centralised runtime & build config
│ └─ app.config.ts
│
├─ 🌐i18n # Next-intl configuration
│ └─ messages # Translations /en, /de
│
├─ 🧩modules # Business domain engines, facades
│ ├─ database
│ ├─ auth
│ ├─ cms
│ └─ ...
│
├─ 📡services # Technical utility wrappers
│ ├─ api.ts
│ └─ store.ts
│
├─ 🎨styles
│ ├─ global.css # Global styles
│ └─ tokens.css # Design tokens
└─
📂 Breakdown
/app - Holds all routes, layouts, and route-specific UI. This is the file-system router entry point. Files inside define public and private pages, error boundaries, loading states, and layouts. Only colocate UI here if it belongs exclusively to that route.
/app/api - Contains API route handlers and webhooks. Each folder inside represents one endpoint. Use this layer only for request handling, delegating logic into modules/ or services/.
/components - Holds reusable UI components. These are framework-agnostic building blocks that do not contain domain logic. Split further into:
- /layouts → wrapper layouts that combine providers and consistent shells.
- /shared → widgets reused across pages or features (navigation, banners, forms).
- /ui → smallest UI parts, often primitives generated from a design system (think for example Shadcn components)
/config - Holds runtime and build configuration. Centralize environment variable parsing, validation, and app-wide constants here. This ensures no process.env usage leaks into components or modules.
/i18n - Holds internationalization resources and setup. Includes message catalogs, locale definitions, and translation helpers. Keeps all localization in one place for consistency.
/modules - Contains business domain logic and facades. Each subfolder represents a domain (auth, database, cms, payments). Inside live server actions, domain schemas, adapters to external systems, and domain-specific hooks. UI imports these modules through public actions, never directly from adapters.
/services - Holds technical utilities and infrastructure wrappers. Examples: API clients, logging setup, analytics SDK wrappers, state management stores. These files describe how the system communicates, not what the business does.
/styles - Contains global styling resources. This includes the main global stylesheet and design tokens. Component or feature-specific styles should remain colocated with those components instead of here.
🔑 Principles to keep this structure maintainable
1. Co-locate until re-used
Keep components, hooks, and utilities close to the route, feature or component that uses them. For example, if a hook is used only on checkout page, keep it there. Only “lift it up” one level, (or into root components/ or modules/) once it’s shared across multiple routes. This avoids premature abstraction and keeps things tidy.
Another benefit of co-location is that it keeps refactors local (PR's tidier) and prevents a monolithic files such as "types.ts" or "helpers.ts" in the root. You will avoid the "junk drawer" pattern, having giant folder or file, where we tend to put things if we don't know where they belong.
2. Keep routing files pure
Purpose of the app/, is to be a file-system router. It should mainly contain Next.js routing files such as: page.tsx, layout.tsx, error.tsx, loading.tsx, etc., and colocated UI that is exclusive to that route - never used outside of that route.
Domain logic, API clients, and reusable UI should live in it's own folders higher in the folder hierarchy.
3. Use route groups for context and separation
Route groups "(...)" don't affect URL path, so use that to your advantage. They help you organize related routes together, for a cleaner hierarchy.
├─ (auth) # Routes related to authentication
│ ├─ login
│ ├─ register
│ └─ forgot-password
│
├─ (checkout) # Routes related to checkout
│ ├─ cart
│ ├─ payment
│ └─ shopping-bag
│
├─ (dashboard) # Top-level context group
│ ├─ (admin) # Sub-level admin related routes
│ └─ (user) # Sub-level user related routes
└─
- Use route groups to mirror functional areas of the app.
- Don’t over-segment → Groups should represent meaningful boundaries (auth, checkout, dashboard), not every small feature.
- Combine with the “co-locate until re-used” principle: if a component is unique to (checkout), keep it inside that group; if reused, promote one level higher.
4. One-way dependency flow
In order to avoid circular imports or bundling server-side libraries to client-side, it's important do define your data flow.
Example:
UI → Services → Modules → External APIs
Note that each layer depends downward, never upward.
Prevents circular imports - if UI imports a module, and the module imports back into UI, you will end up with runtime errors or tangled imports.
Improves testability - you can test modules without mocking UI, and test services without needing domain logic.
Supports scalability - clear separation lets you replace providers or swap frameworks (Axios → Fetch) without touching business logic or UI.
⚡️Putting it all together
Getting the structure right early saves dozens of hours of refactoring later. It shapes the developer experience every single day.
I have turned these ideas into a living, breathing project — FrontendAccelerator.com which comes with this exact structure, clear docs, and a private Discord community where developers share best practices and get feedback on their setup. It’s designed so you don’t have to spend weeks wiring up folders, auth, payments, databases or AI integrations before seeing your app come alive.
If you want to see this setup in action, or building your own SaaS project - check it out.