Bad nextjs folder structure does not show up on day one. It shows up at month six when three developers search for the checkout form hook and find four copies. I reorganised a client dashboard after exactly that — this guide is the tree I use now on large App Router projects, why each folder exists, mistakes from my first Next.js apps, and the 10-second findability rule.
Real folder tree — production-shaped layout
my-app/
├── app/ # routes only — thin pages
│ ├── (marketing)/ # route group — shared layout, no URL segment
│ │ ├── layout.tsx
│ │ ├── page.tsx
│ │ └── pricing/page.tsx
│ ├── (dashboard)/
│ │ ├── layout.tsx
│ │ └── orders/page.tsx
│ ├── api/ # route handlers
│ │ └── webhooks/stripe/route.ts
│ ├── layout.tsx # root layout
│ └── globals.css
├── components/ # shared UI — buttons, cards, shell
│ ├── ui/
│ └── layout/
├── features/ # business domains — colocated logic
│ ├── auth/
│ │ ├── components/
│ │ ├── hooks/
│ │ └── actions.ts
│ └── orders/
│ ├── components/
│ ├── api.ts
│ └── types.ts
├── lib/ # server + shared utilities
│ ├── db.ts
│ └── env.ts
├── hooks/ # truly global client hooks
├── types/ # global TS types
├── data/ # static data, blog posts list
└── public/
Routes live in app/. Business logic lives in features/. Generic design system pieces live in components/ui. That separation is the whole game.
Why each folder exists
| Folder | Purpose | Do not put here |
|---|---|---|
| app/ | URLs, layouts, loading.tsx | Fat business logic |
| features/ | Domain modules (orders, auth) | Generic Button |
| components/ui | Reusable primitives | Order-specific tables |
| lib/ | DB clients, env validation | React components |
| app/api | Webhooks, REST edge cases | Every form POST (prefer actions) |
Thin pages — route files under 40 lines
// app/(dashboard)/orders/page.tsx — orchestration only
import { OrderTable } from "@/features/orders/components/OrderTable";
import { getOrders } from "@/features/orders/api";
export default async function OrdersPage() {
const orders = await getOrders();
return (
<section>
<h1>Orders</h1>
<OrderTable rows={orders} />
</section>
);
}
If your page file exceeds a screen, extract to features. Pages should read like a table of contents.
Thin pages also make Server Component boundaries obvious: data fetching stays in api.ts or the page, UI in feature components, client interactivity in a single leaf with "use client". When a page mixes all three, reviews slow down and hydration bugs hide in the middle of a 200-line file.
The 10-second findability rule
The rule applies to you six months later, not only new hires. I have opened my own repos and lost minutes searching for a webhook handler buried under utils — that shame is why I enforce feature folders now.
Ask: "Where is the code that sends the password reset email?" If you cannot answer in ten seconds, the structure failed. Correct answer in my tree: features/auth/actions.ts or features/auth/emails.ts — not scattered across utils, hooks, and app/api.
// Naming convention that helps findability
features/orders/
components/ → OrderTable.tsx, OrderFilters.tsx
hooks/ → useOrderFilters.ts (client)
api.ts → getOrders, createOrder (server-safe)
types.ts → Order, OrderStatus
First project mistakes I made
components/ dump with 80 files and no subfolders. utils.ts at 1,200 lines. Duplicating fetch logic in every page instead of feature api modules. Putting Server Actions inside random page files instead of colocated actions.ts.
Another mistake: mirroring REST URLs in folder names under components — components/api/users/get.tsx — which fights App Router conventions. Routes belong in app/; business logic belongs in features/. Mixing them creates two sources of truth for the same URL.
// BEFORE — everything in components/
components/
OrderTable.tsx
UserAvatar.tsx
pricing-card.tsx
helper.ts
// AFTER — domain colocation
features/orders/components/OrderTable.tsx
features/users/components/UserAvatar.tsx
app/(marketing)/pricing/page.tsx // thin
Route groups for different shells
// app/(marketing)/layout.tsx — public nav, footer
// app/(dashboard)/layout.tsx — sidebar, auth gate
// URL stays /pricing and /orders — parentheses omit segment
Pair with rendering choices from SSR vs SSG vs ISR guide — marketing group ISR, dashboard group client-heavy.
Path aliases and import direction
// tsconfig paths — "@/": ["./"]
// Allowed import flow:
// app → features → components/ui → lib
// features → lib
// Never: lib → features (inverts layers)
Enforce with ESLint import rules when the team grows past three people.
I also keep a single README.md at the repo root with a one-paragraph map: where routes live, where features live, how to run migrations. The tree in this article is the visual version of that map — new contractors read it before their first PR.
My production setup
In production on this portfolio and client sites: route groups for marketing vs app shells, features/ for blog and contact logic, components/ui for sparkles and buttons. TypeScript strict — see strict mode guide. New engineers onboard by reading features/ first, not app/ line by line.
At my day job, we added a CODEOWNERS file per feature folder — reviews stay scoped. Structure is team policy, not personal taste.
When a feature folder grows past ~15 files, split subdomains — features/orders/checkout/ vs features/orders/history/ — before you invent a second top-level folder that duplicates the domain name.
The single takeaway
Routes in app/, domains in features/, primitives in components/ui. If findability fails the 10-second test, refactor before adding the next feature.
Related: App Router beginner guide. Contact.
If this helped you
I publish free tutorials and write-ups like this in my spare time — no paywall on the guides. If it saved you an afternoon of trial and error, you can support the work:
- → Buy me a coffee at buymeacoffee.com/safdarali
- 👉 Subscribe to my YouTube channel — it's free; 70+ React & Next.js tutorials
Related reading
More guides on safdarali.in — same author, production-focused.
-
How to Build a Frontend Developer Portfolio That Stands Out
Frontend developer portfolio guide for India — sections, React/Next.js examples, SEO, performance, personal branding, FAQ, and checklist to build and rank.
May 2026 · Read article →
-
React Server Components vs Client Components — When to Use Which
Practical RSC vs client guide for Next.js App Router — when to use each, real code, bundle before/after, and performance impact.
May 2026 · Read article →
Top comments (0)