BACK TO BLOG
COVER IMAGE
A Practical Guide to Next.js Project Structure
Development
July 10, 20268 min read

A Practical Guide to Next.js Project Structure

How I think about organizing components, features, data, and routes when building larger Next.js applications.

Next.jsArchitectureTypeScript

When a Next.js project grows past a certain point — somewhere around ten routes and fifteen components — the default structure starts to feel cramped. Files get lost. Imports become guessing games. Refactoring turns into archaeology.

After working on several production Next.js applications, I have settled on a structure that scales well without being overly prescriptive. It is not a framework — just a set of sensible defaults that can flex when the project demands it.

The Top-Level Structure

Project Rootbash
src/
├── app/              # Next.js App Router (routes only)
│   ├── (main)/       # Grouped layout routes
│   │   ├── about/
│   │   ├── blogs/
│   │   └── work/
│   ├── api/          # API route handlers
│   ├── globals.css
│   └── layout.tsx
├── components/       # Shared, reusable UI components
├── screens/          # Page-level screen components
├── data/             # Static JSON data files
├── hooks/            # Custom React hooks
├── lib/              # Utility functions & helpers
├── services/         # API client functions
└── providers/        # Context & global providers

The app/ Directory — Routes Only

With the App Router, I treat the app/ directory as a routing layer only. Page files stay thin — they import a screen component, pass props, and render. No business logic, no fetch logic, no JSX beyond what is strictly necessary for the layout.

app/(main)/blogs/page.tsxtsx
import { BlogScreen } from '@/screens/blogs/BlogScreen';
import blogsData from '@/data/blogs.json';

export default function BlogPage() {
  return <BlogScreen blogs={blogsData.blogs} />;
}

Screens vs Components

This is the distinction that saves the most confusion. A component is reusable anywhere. A screen is a full-page layout that belongs to one specific route.

TABLE — When to put something in components/ vs screens/
Questioncomponents/screens/
Used in more than one page?YesNo
Contains page-level layout?NoYes
Has specific route context?NoYes
Generic enough to reuse?YesNo
When to put something in components/ vs screens/

Data Files

For portfolio sites, personal projects, and small applications, static JSON in data/ is often the right call. No database overhead, no API layer, full type safety with TypeScript imports.

  • Keep one concern per file (projects.json, blogs.json, etc.)
  • Export arrays under a named key, not as a root array
  • Co-locate TypeScript types in a types/ or lib/ file
  • Validate shape at the import level with Zod if data is dynamic
tsconfig.json (paths section)json
{
  "compilerOptions": {
    "paths": {
      "@/*": ["./src/*"]
    }
  }
}

The best project structure is the one your team can navigate without asking questions.

A principle worth remembering

None of this is absolute. The structure above reflects what works for my projects. Yours might need something different. The goal is not to follow a template — it is to make intentional decisions early, so you spend less time reorganizing and more time building.

Abdullah Al Maksud

WRITTEN BY

Abdullah Al Maksud

Developer, designer, writer.

/ RELATED

MORE IN DEVELOPMENT

AM

© 2026 Abdullah Al Maksud
All rights reserved.

QUICK LINKS

LET'S CONNECT

Open to interesting projects and opportunities.

SAY HELLO 👋
Designed & Built by Abdullah Al MaksudDhaka, Bangladesh 🇧🇩