Technology · Next.js

Next.js File Structure

Organize Next.js projects for maintainability and performance.

TL;DR
  1. 01Use app router for file-based routing with folders.
  2. 02Group related files with parentheses to exclude from routing.
  3. 03Colocate components, hooks, and utilities near where they're used.

App Router Basics

  • Create pages by adding folders and page.tsx files.
    app/
      page.tsx              # / route
      about/
        page.tsx            # /about route
      blog/
        page.tsx            # /blog route
        [slug]/
          page.tsx          # /blog/:slug route
  • Files become routes automatically based on folder structure.
  • Each folder represents a path segment.
    app/users/[id]/posts/page.tsx → /users/:id/posts
  • Add a root layout.tsx that wraps every page in the app.
    // app/layout.tsx
    export default function RootLayout({ children }) {
      return <html lang="en"><body>{children}</body></html>;
    }
  • Use page.tsx for visible pages and route.ts for API handlers.
    app/
      api/
        users/
          route.ts          # GET /api/users, POST /api/users
      users/
        page.tsx            # /users page
  • Add a loading.tsx next to any page to show a Suspense fallback.
    // app/blog/loading.tsx
    export default function Loading() {
      return <p>Loading posts...</p>;
    }

Special Files

  • Use layout.tsx for shared UI across routes.
    // app/layout.tsx (root layout)
    export default function RootLayout({ children }) {
      return (
        <html>
          <body>
            <Header />
            {children}
            <Footer />
          </body>
        </html>
      );
    }
  • Use error.tsx for error boundaries.
    // app/blog/error.tsx
    export default function Error({ error, reset }) {
      return (
        <div>
          <h2>Something went wrong</h2>
          <button onClick={() => reset()}>Try again</button>
        </div>
      );
    }
  • Use loading.tsx for Suspense fallbacks.
    // app/blog/loading.tsx
    export default function Loading() {
      return <p>Loading posts...</p>;
    }
  • Use not-found.tsx to show a custom 404 page per segment.
    // app/blog/[slug]/not-found.tsx
    export default function NotFound() {
      return <h1>Post not found</h1>;
    }
  • Use template.tsx when you need the layout to re-mount on each navigation.
    // app/dashboard/template.tsx
    export default function Template({ children }) {
      return <div key={Math.random()}>{children}</div>;
    }

Route Groups

  • Use parentheses to organize without affecting routes.
    app/
      (marketing)/
        page.tsx            # / (still at root)
        about/page.tsx      # /about
        contact/page.tsx    # /contact
      (dashboard)/
        layout.tsx          # Separate layout
        page.tsx            # /dashboard
  • Group routes for shared layouts.
    app/
      (auth)/
        layout.tsx          # Auth layout
        login/page.tsx      # /login
        signup/page.tsx     # /signup
      (app)/
        layout.tsx          # App layout
        dashboard/page.tsx  # /dashboard
  • Apply middleware only to grouped routes using route group naming.
    app/
      (protected)/
        dashboard/page.tsx
        settings/page.tsx
      (public)/
        page.tsx
        about/page.tsx
  • Each route group can have its own layout without URL impact.
    // app/(auth)/layout.tsx
    export default function AuthLayout({ children }) {
      return <main className="auth-bg">{children}</main>;
    }
  • Combine route groups with parallel routes for complex UI.
    app/
      (dashboard)/
        @analytics/page.tsx
        @overview/page.tsx
        layout.tsx

Component Organization

  • Colocate components near their usage.
    app/
      page.tsx
      components/
        Hero.tsx
        CTA.tsx
      blog/
        page.tsx
        components/
          PostCard.tsx
          PostList.tsx
  • Create shared utility functions and hooks.
    app/
      lib/
        db.ts
        utils.ts
      hooks/
        useFetch.ts
        useAuth.ts
      components/
        Button.tsx
  • Use lib folder for utilities shared across routes.
  • Store global types in a types folder at the root.
    src/
      types/
        api.ts
        user.ts
        post.ts
  • Use a ui folder for design system components like buttons and cards.
    app/
      ui/
        Button.tsx
        Card.tsx
        Modal.tsx
        index.ts   # export all

Scaling Patterns

  • Use feature folders for larger projects.
    app/
      features/
        auth/
          page.tsx
          components/
          hooks/
          utils.ts
        posts/
          page.tsx
          components/
          hooks/
        profile/
          page.tsx
  • Keep related files together by feature.
    features/users/
      page.tsx
      layout.tsx
      error.tsx
      loading.tsx
      components/
        UserCard.tsx
      hooks/
        useUser.ts
      lib/
        db.ts
  • Move shared utilities to root lib folder.
  • Use barrel files to simplify imports from feature folders.
    // features/auth/index.ts
    export { LoginForm } from "./components/LoginForm";
    export { useAuth } from "./hooks/useAuth";
  • Place server actions in a dedicated actions folder per feature.
    features/posts/
      actions/
        createPost.ts
        deletePost.ts
      components/
      page.tsx

Tip: Organize by feature or domain first, not by file type — this keeps related code together and makes refactoring easier.

Warning: Don't create too many deeply nested folder levels — keep structures 3–4 levels deep for clarity.

Next.js Data FetchingNext.js Font Optimization