nextjs
This skill should be used every time you work on Next.js projects or Next.js app code changes in this repository.
SKILL.mdskills/software-engineering/nextjs/SKILL.md
GitHubNext.js Project Structure Rules
This file defines the preferred Next.js project organization for this repo, constrained to folder names that appear in the official Bulletproof React project structure.
Core Principles
- Organize by feature and domain, not by file type.
- Keep app-level orchestration in
app/only. - Treat
features/as the primary unit of change and ownership. - Share across features through top-level
components/,hooks/,lib/,types/,utils/, andconfig/, never by reaching into another feature. - Keep feature boundaries explicit: local UI, logic, API calls, state, types, and utilities live together.
- Always use
next/linkfor internal navigation - never use<a href="...">for internal routes.
Server Logging (MANDATORY)
- All server logs must be written to a gitignored project-local directory.
- Use
pinoas the approved server logging library.
E2E Testing (MANDATORY)
- Browser/runtime E2E testing must always use the
agent-browserskill. - The
agent-browserrequirement is strict; do not substitute ad-hoc/manual browser testing. - If a server is already running, do not kill it just to rerun tests.
Tailwind CSS v4 (MANDATORY)
Tailwind CSS v4 is the only permitted CSS framework.
Strict Rules
- v4 only - use current Tailwind v4 setup, not older Tailwind patterns.
- No CSS-in-JS - no styled-components, Emotion, or CSS modules.
- No inline styles - use Tailwind utility classes.
- Component patterns - compose complex styles with utility classes instead of custom class systems.
- Arbitrary values - use square bracket notation for one-off values such as
w-[300px]. - clsx/tailwind-merge - use
clsxandtailwind-mergefor conditional classes.
Anti-Patterns (NEVER DO THESE)
- Using styled-components, Emotion, or similar
- Using
@applyto extract classes - Using inline
styleprops - Mixing Tailwind with other CSS solutions
Top-Level Layout
src/
├── app/ # application layer
├── assets/ # static assets
├── components/ # shared components
├── config/ # global configuration
├── features/ # feature based modules
├── hooks/ # shared hooks
├── lib/ # reusable libraries
├── stores/ # global state stores
├── testing/ # test utilities and mocks
├── types/ # shared types
└── utils/ # shared utility functions
Folder Semantics
app/: application glue and composition only.routes/,app.tsx,provider.tsx,router.tsx
features/: each feature is a self-contained folder.features/awesome-feature/
components/: shared components used across the application.hooks/: shared hooks used across the application.lib/: reusable libraries preconfigured for the application.types/: shared types used across the application.utils/: shared utility functions.config/: global configuration and exported environment variables.assets/: static assets such as images and fonts.stores/: global state stores.testing/: test utilities and mocks.
Feature Structure (Bulletproof)
Each feature owns its UI, logic, and API integration. Add only what the feature needs.
src/features/awesome-feature/
├── api/ # exported API request declarations and API hooks
├── assets/ # feature-specific static assets
├── components/ # feature-scoped UI
├── hooks/ # feature-scoped hooks
├── stores/ # feature state stores
├── types/ # feature types
└── utils/ # feature utilities
Feature Size And Atomicity
Features must stay small, focused, and explainable in one sentence. Avoid turning a single feature into a god-folder that owns multiple unrelated responsibilities.
When a feature grows too large, split it into smaller atomic features using flat names under features/:
features/<feature-name>-<subfeature>/
This keeps boundaries explicit without introducing an extra architectural layer that Bulletproof React does not define.
Good examples:
features/chat-panel-transcript/features/chat-panel-sidebar/features/chat-panel-summary/features/conversation-list/features/conversation-search/
Avoid splitting by arbitrary file type or low-level implementation detail.
Less useful examples:
features/chat-panel-buttons/features/chat-panel-utils/features/chat-panel-misc/
Use a separate feature when the code has its own UI, logic, state, API interaction, or user-facing responsibility. Keep code nested inside an existing feature when it is only a private implementation detail of that feature.
Rules of thumb:
- If a feature has multiple distinct user-facing responsibilities, split it.
- If a folder needs many unrelated components, hooks, stores, and types, split it.
- If a piece can be understood, tested, and replaced on its own, it can likely be its own feature.
- Keep cross-feature imports forbidden; compose features at the
app/layer.
File Naming
- Use
kebab-casefor folders and files. - Filename matches the primary component or concern.
- One logical concern per file.
Import Rules
features/can import fromcomponents/,hooks/,lib/,types/,utils/,config/, andstores/.components/,hooks/,lib/,types/,utils/,config/, andstores/never import fromfeatures/.- Cross-feature imports are forbidden; compose features at the
app/layer.
Example Structure
src/
├── app/
│ ├── routes/
│ ├── app.tsx
│ ├── provider.tsx
│ └── router.tsx
├── assets/
├── components/
├── config/
├── features/
│ └── awesome-feature/
│ ├── api/
│ ├── assets/
│ ├── components/
│ ├── hooks/
│ ├── stores/
│ ├── types/
│ └── utils/
├── hooks/
├── lib/
├── stores/
├── testing/
├── types/
└── utils/