nextjs-enterprise-init
Next.js Enterprise Project Initialization
Initialize Next.js projects with enterprise-grade architecture and conventions.
Quick Start
To initialize a Next.js project with enterprise structure:
python3 scripts/init_nextjs_structure.py /path/to/project
The script will:
- Create
src/common/with utilities, components, hooks, services - Create
src/features/with example feature module - Set up proper TypeScript path aliases
- Create global styles with CSS variables for theming
- Generate example API routes
Architecture Overview
This skill implements a feature-first architecture with clear separation of concerns:
src/
├── app/ # Next.js App Router (routes, layouts, pages)
├── features/ # Feature-based modules (business logic)
├── common/ # Shared utilities and components
└── styles/ # Global styles
Key Principles:
- Feature-first: Organize by business domain, not technical layers
- Separation of concerns: UI, logic, and data fetching are separate
- Dependency direction:
app/→features/→common/(one-way) - Atomic components: Build complex UIs from simple, reusable parts
Directory Structure
For complete directory structure details, see structure.md.
Key Directories
| Directory | Purpose |
|---|---|
app/ |
Next.js App Router - routes, layouts, pages, API routes |
features/ |
Feature modules - self-contained business logic |
common/components/ui/ |
Atomic UI components (Button, Input, etc.) |
common/hooks/ |
Shared React hooks |
common/services/ |
API services and data fetching |
common/utils/ |
Pure utility functions |
common/config/ |
Configuration (theme, i18n) |
Component Conventions
Atomic Component Pattern
Each component lives in its own directory:
ComponentName/
├── ComponentName.tsx # Main component
├── ComponentName.module.css # Styles
├── ComponentName.test.tsx # Tests
└── index.ts # Exports
Size Guidelines:
- Atomic: ≤150 lines (Button, Input)
- Composite: 150-300 lines (composed of atomics)
- Complex: >300 lines (split into sub-components)
Component Declaration
Always use function components with explicit types:
import { ButtonProps } from './Button.types';
import styles from './Button.module.css';
export function Button({ variant = 'primary', children, ...props }: ButtonProps) {
return (
<button className={`${styles.button} ${styles[variant]}`} {...props}>
{children}
</button>
);
}
Hooks Conventions
Custom Hook Pattern
// useUserProfile.ts
export function useUserProfile(userId: string) {
const [state, setState] = useState<AsyncState<User>>({ status: 'idle' });
// ... implementation
return {
user: state.status === 'success' ? state.data : null,
loading: state.status === 'loading',
error: state.status === 'error' ? state.error : null,
refetch,
};
}
Rules:
- Prefix with
use - Return stable object (use useMemo if needed)
- Handle cleanup in useEffect
- Specify all dependencies
API & Services
API Client Pattern
Use the base apiClient from common/services/apiClient.ts:
import { apiClient } from '@/common/services/apiClient';
export const userApi = {
async getById(id: string): Promise<User> {
return apiClient.get<User>(`/users/${id}`);
},
async update(id: string, data: UserUpdateData): Promise<User> {
return apiClient.put<User>(`/users/${id}`, data);
},
};
State Management Strategy
| State Type | Solution | Example |
|---|---|---|
| Server State | React Query | User data, API responses |
| URL State | URL params/search | ?page=2&filter=active |
| Form State | Component state | Input values, validation |
| Global UI | Context | Theme, language, auth status |
Styling Conventions
CSS Modules (Preferred)
import styles from './Button.module.css';
export function Button({ variant = 'primary' }: ButtonProps) {
return <button className={`${styles.button} ${styles[variant]}`}>...</button>;
}
Theming with CSS Variables
:root {
--color-primary: #0070f3;
--spacing-md: 1rem;
--radius-md: 8px;
}
[data-theme='dark'] {
--color-primary: #3291ff;
}
For detailed styling patterns, see conventions.md.
Feature Module Structure
Each feature is self-contained:
features/feature-name/
├── components/ # Feature-specific components
├── hooks/ # Feature-specific hooks
├── services/ # Feature-specific API calls
├── types/ # Feature-specific types
└── index.ts # Public API exports
Feature conventions:
- Export only public API via
index.ts - Keep feature-specific code within the feature
- Use barrel exports for clean imports
Import Conventions
Order imports consistently:
// 1. React and Next.js
import { useState } from 'react';
import Link from 'next/link';
// 2. Third-party libraries
import { clsx } from 'clsx';
// 3. Internal imports - features
import { UserProfile } from '@/features/user-profile';
// 4. Internal imports - common
import { Button } from '@/common/components/ui/Button';
import { useDebounce } from '@/common/hooks/useDebounce';
File Naming Conventions
- Components: PascalCase (UserProfile.tsx)
- Hooks: camelCase with 'use' prefix (useUserProfile.ts)
- Utilities: camelCase (formatDate.ts)
- Types: *.types.ts suffix (user.types.ts)
- Tests: *.test.tsx or *.spec.tsx
Theming Extensibility
The theme system supports extension:
import { extendTheme, lightTheme } from '@/common/config/theme';
const customTheme = extendTheme(lightTheme, {
colors: {
primary: '#custom-color',
},
});
Theme config is in common/config/theme.ts - see architecture.md for details.
i18n Extensibility
Add new locales by:
- Adding locale to
i18nConfig.localesincommon/config/i18n.ts - Creating translation file in
common/locales/{locale}.json
No code changes required for new locales.
Error Handling
Async Error Handling Pattern
export function UserProfile() {
const [state, setState] = useState<AsyncState<User>>({ status: 'idle' });
useEffect(() => {
const fetchUser = async () => {
setState({ status: 'loading' });
try {
const user = await userApi.getById(userId);
setState({ status: 'success', data: user });
} catch (error) {
setState({
status: 'error',
error: error instanceof Error ? error : new Error('Unknown error'),
});
}
};
fetchUser();
}, [userId]);
if (state.status === 'error') {
return <ErrorMessage error={state.error} />;
}
// ...
}
Testing Conventions
Test Structure
import { render, screen } from '@testing-library/react';
import { describe, it, expect, vi } from 'vitest';
import { Button } from './Button';
describe('Button', () => {
it('renders children correctly', () => {
render(<Button>Click me</Button>);
expect(screen.getByText('Click me')).toBeInTheDocument();
});
});
Security Considerations
- Never expose secrets - Use environment variables
- Validate user input - Client and server-side
- Sanitize user-generated content - Use DOMPurify for HTML
- Implement rate limiting - For API routes
- Use CSRF protection - For form submissions
CLAUDE.md
Generate CLAUDE.md in project root using the template from assets/CLAUDE.md.template.
The CLAUDE.md should contain:
- Project overview
- Architecture summary
- Development guidelines
- Project-specific rules
- Common tasks
Using the Initialization Script
The script handles the complete setup:
# Run from project root
python3 scripts/init_nextjs_structure.py
# Or specify project path
python3 scripts/init_nextjs_structure.py /path/to/nextjs/project
What it creates:
src/common/with full subdirectory structuresrc/features/with example featuresrc/app/api/with example API route- Global CSS with theme variables
- Updated
tsconfig.jsonwith path aliases
Additional References
- structure.md - Complete directory structure
- conventions.md - Detailed coding standards
- architecture.md - Architecture principles and patterns
Load these files when you need detailed information about specific aspects of the project structure.