Skip to main content

project-structure

Next.js project architecture and file organization patterns. Use when the user asks about "Next.js project structure", "folder organization", "feature-based structure", "where to put shared code", "naming conventions", "barrel exports", "modular architecture", "colocation", "route groups for organization", or needs guidance on organizing a scalable Next.js codebase.

Source facts

Repository
Agents-Store/opencode-plugins
Last source activity
August 18, 2026 at 12:05
Detected SKILL.md language
English
Stars
0
Forks
0

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
project-structure
description
Next.js project architecture and file organization patterns. Use when the user asks about "Next.js project structure", "folder organization", "feature-based structure", "where to put shared code", "naming conventions", "barrel exports", "modular architecture", "colocation", "route groups for organization", or needs guidance on organizing a scalable Next.js codebase.
# Project Structure & Organization Next.js is **unopinionated** about how you organize your project. This skill provides proven patterns for small, medium, and large applications. ## File Conventions (Next.js 16) These files have special meaning inside `app/`: | File | Purpose | |------|---------| | `layout.tsx` | Shared UI wrapping child segments (persists across navigation) | | `page.tsx` | Public route UI | | `loading.tsx` | Loading UI (Suspense boundary) | | `error.tsx` | Error UI (error boundary) — see `error-handling` skill | | `not-found.tsx` | 404 UI | | `route.ts` | API endpoint (Route Handler) | | `template.tsx` | Re-rendered layout (new instance on navigation) | | `default.tsx` | Parallel route fallback | | `global-error.tsx` | Root error boundary (must include `<html>`, `<body>`) | Top-level files: | File | Purpose | |------|---------| | `next.config.js` / `next.config.ts` | Next.js configuration | | `proxy.ts` | Request proxy (replaces middleware in Next.js 16) | | `instrumentation.ts` | OpenTelemetry and instrumentation | | `.env.local` | Local environment variables | ## Recommended Structures ### Small App (< 10 routes) Keep everything colocated in `app/`. Shared code in top-level folders: ``` src/ ├── app/ │ ├── layout.tsx │ ├── page.tsx │ ├── about/ │ │ └── page.tsx │ └── blog/ │ ├── page.tsx │ └── [slug]/ │ └── page.tsx ├── components/ │ ├── header.tsx │ └── footer.tsx ├── lib/ │ └── utils.ts └── public/ └── images/ ``` ### Medium App (10–30 routes) Use **route groups** for organizational separation and colocate related code: ``` src/ ├── app/ │ ├── layout.tsx │ ├── page.tsx │ ├── (marketing)/ │ │ ├── layout.tsx │ │ ├── about/page.tsx │ │ ├── blog/page.tsx │ │ └── pricing/page.tsx │ ├── (dashboard)/ │ │ ├── layout.tsx │ │ ├── dashboard/page.tsx │ │ ├── settings/page.tsx │ │ └── _components/ # Private folder — not routable │ │ ├── sidebar.tsx │ │ └── nav.tsx │ └── api/ │ └── webhooks/ │ └── route.ts ├── components/ # Shared UI components │ ├── ui/ # Primitives (button, input, card) │ └── shared/ # Composed components (data-table, file-upload) ├── lib/ │ ├── db.ts # Database client │ ├── utils.ts # Generic utilities │ └── constants.ts ├── actions/ # Server Actions │ ├── auth.ts │ └── posts.ts └── types/ └── index.ts ``` ### Large App (30+ routes) Use **feature-based organization** — group by domain, not by technical layer: ``` src/ ├── app/ │ ├── layout.tsx │ ├── page.tsx │ ├── (auth)/ │ │ ├── login/page.tsx │ │ └── register/page.tsx │ ├── (dashboard)/ │ │ ├── layout.tsx │ │ └── [...slug]/page.tsx │ └── api/ │ └── webhooks/stripe/route.ts ├── features/ │ ├── auth/ │ │ ├── components/ │ │ │ ├── login-form.tsx │ │ │ └── register-form.tsx │ │ ├── actions/ │ │ │ └── auth-actions.ts │ │ ├── lib/ │ │ │ └── session.ts │ │ └── types.ts │ ├── posts/ │ │ ├── components/ │ │ │ ├── post-card.tsx │ │ │ └── post-list.tsx │ │ ├── actions/ │ │ │ └── post-actions.ts │ │ ├── queries/ │ │ │ └── get-posts.ts │ │ └── types.ts │ └── billing/ │ ├── components/ │ ├── actions/ │ ├── lib/ │ └── types.ts ├── components/ # Truly shared UI only │ └── ui/ ├── lib/ # Truly shared utilities only │ ├── db.ts │ └── utils.ts └── types/ └── globals.d.ts ``` ## Key Organization Patterns ### Route Groups `(name)` Wrap folders in parentheses to organize without affecting URLs: ``` app/ ├── (marketing)/ # URL: /about, /blog — NOT /marketing/about │ ├── layout.tsx # Marketing-specific layout │ ├── about/page.tsx │ └── blog/page.tsx ├── (shop)/ # URL: /cart, /products │ ├── layout.tsx # Shop-specific layout (with cart sidebar) │ ├── cart/page.tsx │ └── products/page.tsx ``` Each route group can have its own `layout.tsx`, `loading.tsx`, and `error.tsx`. ### Private Folders `_name` Prefix with underscore to exclude from routing. Safe for colocated utilities: ``` app/dashboard/ ├── page.tsx ├── _components/ # NOT a route — colocated UI │ ├── stats-card.tsx │ └── recent-activity.tsx ├── _lib/ # NOT a route — colocated helpers │ └── format-data.ts └── _hooks/ └── use-dashboard-data.ts ``` ### Data Layer Organization Separate data access from UI: ```typescript // src/features/posts/queries/get-posts.ts — Data fetching import { db } from '@/lib/db' export async function getPosts(limit = 10) { return db.post.findMany({ take: limit, orderBy: { createdAt: 'desc' } }) } // src/features/posts/actions/post-actions.ts — Mutations 'use server' import { revalidatePath } from 'next/cache' import { db } from '@/lib/db' export async function createPost(formData: FormData) { await db.post.create({ data: { title: formData.get('title') as string } }) revalidatePath('/posts') } // src/app/posts/page.tsx — Route (thin, delegates to feature) import { getPosts } from '@/features/posts/queries/get-posts' import { PostList } from '@/features/posts/components/post-list' export default async function PostsPage() { const posts = await getPosts() return <PostList posts={posts} /> } ``` ## Naming Conventions | Convention | Example | Notes | |------------|---------|-------| | Files | `kebab-case.tsx` | `post-card.tsx`, `auth-actions.ts` | | Components | `PascalCase` | `PostCard`, `LoginForm` | | Hooks | `camelCase` with `use` prefix | `useDashboardData` | | Server Actions | `camelCase` verbs | `createPost`, `deleteUser` | | Types | `PascalCase` | `Post`, `UserSession` | | Constants | `SCREAMING_SNAKE_CASE` | `MAX_FILE_SIZE`, `API_URL` | | Route folders | `kebab-case` | `blog-posts/`, `user-settings/` | ## Path Aliases Configure in `tsconfig.json` for clean imports: ```json { "compilerOptions": { "paths": { "@/*": ["./src/*"], "@/components/*": ["./src/components/*"], "@/features/*": ["./src/features/*"], "@/lib/*": ["./src/lib/*"] } } } ``` ## Anti-patterns - **"Utils" dumping ground** — Don't create a catch-all `utils/` with 50+ files. Split by domain or move into features. - **Deep barrel exports** — Avoid `index.ts` re-exporting from deeply nested files. Causes circular imports and bundle bloat. - **Mixing concerns in `app/`** — Don't put business logic, database queries, or utility functions directly in route files. Route files should be thin orchestrators. - **Unnecessary abstraction** — Don't create `features/` for a 5-page app. Start simple, extract when it hurts. - **Importing server code in client components** — Use `server-only` package to prevent accidental leaks. See `security-patterns` skill. ## What This Skill Does NOT Cover - Monorepo setup with Turborepo (tool-specific) - CI/CD and deployment configuration (see `cli-recipes`) - Routing patterns like parallel/intercepting routes (see `app-router-patterns`) - Component architecture decisions (see `server-client-components`)
View on GitHub