My Ideal Next.js Project Structure in 2025
When starting a new Next.js project, one of the first (and most underrated) decisions you’ll make is how to structure your files and folders. And in 2025—with App Router, edge functions and hybrid rendering options—this matters more than ever.
Category
Development
Author
Aleksi Huusko
Published
18.7.2025
Updated
25.7.2025
Share

Summary
- Use a clear, modular folder structure with /app, /components, /lib, /server, /types, and /config to separate routing, UI, logic, server actions, and types cleanly.
- Leverage the App Router with layouts, route groups, and server components as the default, keeping client components isolated where needed.
- Organize components by function, not type, and use libraries like Tailwind + shadcn/ui for fast and scalable UI development.
- Maintain strict conventions and tooling (TypeScript, ESLint, Prettier, CI) to ensure consistency, clarity, and team scalability.
How I organize real-world projects for clarity, scalability and speed?
After building projects for clients, experiments, and startups, I’ve refined a system that helps me work faster, stay organized, and avoid painful refactors later. Here’s how I structure my Next.js apps today—and why.
The big picture
Here’s the top-level folder structure I aim for in most projects:
/app
/components
/lib
/server
/types
/styles
/public
/config
Let’s break it down.
/app: Routes, Layouts & Pages
Next.js 13+ introduced the App Router, and it’s now the default for new projects. I treat /app as the entry point for routing and page-specific logic.
- Each route has its own folder (/app/about, /app/dashboard)
- I use layout.tsx for persistent UIs like sidebars or navs
- loading.tsx and error.tsx for handling async boundaries
- Server components by default; I mark client components explicitly
Bonus: I keep route groups clean with (marketing) or (auth) folders to separate flows without affecting the URL.
/components: Reusable UI Parts
I group components by role, not by type. Example:
/components
/ui → buttons, inputs, modals (low-level building blocks)
/sections → homepage hero, pricing blocks, footers
/layout → headers, navigation, sidebars
I avoid over-abstracting. If a component is only used once in a single route, I keep it close to the route folder itself.
Tip: For shared UI, I often use shadcn/ui with Tailwind for fast prototyping and clean design tokens.
/lib: Utilities, Helpers, and Hooks
Everything that’s logic-related but not part of rendering goes into lib.
- lib/validation.ts → Zod schemas
- lib/utils.ts → string formatters, date helpers
- lib/hooks/useDebounce.ts → custom hooks
I use this folder for anything that’s used across multiple parts of the app but doesn’t belong to any single route or UI module.
/server: Actions, Services & DB Access
This folder is key if you’re using server actions, API integrations, or ORMs like Drizzle.
- server/actions/ → async server actions (form submissions, mutations)
- server/db/ → schema definitions and queries
- server/services/ → external API wrappers (e.g. Stripe, OpenAI)
Keeping server logic here helps keep the /app folder focused on UI and routing.
/types: TypeScript Contracts
I define all global types and interfaces here, especially if shared across server and client:
- types/user.ts
- types/project.ts
I also use zod schemas and infer types directly when possible for safety and clarity.
/styles and /public
- I use Tailwind CSS, often with a globals.css and tailwind.config.ts
- I still keep /styles/variables.css if I need to define global themes
- All static assets (images, favicons, OG images) go into /public
/config: Environment & Service Configs
This is where I keep:
- config/site.ts → metadata, SEO defaults, site name
- config/stripe.ts or config/next-auth.ts for service config
This helps centralize setup and avoids magic strings across the codebase.
Bonus: Naming, Linting, and Conventions
- I use kebab-case for folders and PascalCase for components
- ESLint + Prettier + TypeScript strict mode are enabled from the start
- I lint both client and server separately if needed
- Commit conventions (feat:, fix:, etc.) + GitHub Actions for CI
Final Thoughts
Project structure isn’t just about cleanliness—it’s about velocity. The way I structure my Next.js apps in 2025 helps me:
- Find files instantly
- Avoid duplicate logic
- Keep server and client concerns separated
- Onboard collaborators faster
- Scale projects without chaos
The best part? It’s flexible. You can grow it over time—start simple and evolve as your app does.