Loading...
Loading...
Guides React/Next.js/TypeScript project organization using feature-based architecture. Use when structuring new projects, reorganizing codebases, or deciding where to place new code.
npx skill4agent add flpbalada/my-opencode-config project-structuresrc/
├── app/ # Application layer (routing, providers)
│ ├── routes/ # Route definitions / pages
│ ├── app.tsx # Main application component
│ ├── provider.tsx # Global providers wrapper
│ └── router.tsx # Router configuration
├── assets/ # Static files (images, fonts)
├── components/ # Shared UI components
├── config/ # Global configuration, env variables
├── features/ # Feature-based modules (main code lives here)
├── hooks/ # Shared hooks
├── lib/ # Pre-configured libraries (axios, dayjs, etc.)
├── stores/ # Global state stores
├── testing/ # Test utilities and mocks
├── types/ # Shared TypeScript types
└── utils/ # Shared utility functionssrc/features/users/
├── api/ # API requests & React Query hooks
├── components/ # Feature-scoped UI components
├── hooks/ # Feature-scoped hooks
├── stores/ # Feature state (Zustand, etc.)
├── types/ # Feature-specific types
└── utils/ # Feature utility functions┌─────────┐ ┌──────────┐ ┌─────┐
│ shared │ ──► │ features │ ──► │ app │
└─────────┘ └──────────┘ └─────┘| From | Can Import From |
|---|---|
| |
| |
| Other |
| Code Type | Location | Example |
|---|---|---|
| Route/page component | | |
| Feature-specific component | | |
| Reusable UI component | | |
| Feature API calls | | |
| Shared utility | | |
| Feature utility | | |
| Global state | | |
| Feature state | | |
| Library wrapper | | |
| Global types | | |
| Feature types | | |
// .eslintrc.js
module.exports = {
rules: {
'import/no-restricted-paths': [
'error',
{
zones: [
// Disables cross-feature imports
{
target: './src/features/users',
from: './src/features',
except: ['./users'],
},
{
target: './src/features/posts',
from: './src/features',
except: ['./posts'],
},
// Add more features as needed
],
},
],
},
};// .eslintrc.js
module.exports = {
rules: {
'import/no-restricted-paths': [
'error',
{
zones: [
// Features cannot import from app
{
target: './src/features',
from: './src/app',
},
// Shared cannot import from features or app
{
target: [
'./src/components',
'./src/hooks',
'./src/lib',
'./src/types',
'./src/utils',
],
from: ['./src/features', './src/app'],
},
],
},
],
},
};// src/features/users/api/getUsers.ts
import { useQuery } from '@tanstack/react-query';
import { api } from '@/lib/axios';
import type { User } from '../types/user';
export const getUsers = async (): Promise<User[]> => {
const response = await api.get('/users');
return response.data;
};
export const useUsers = () => {
return useQuery({
queryKey: ['users'],
queryFn: getUsers,
});
};// src/features/users/components/UserList.tsx
import { useUsers } from '../api/getUsers';
import { UserCard } from './UserCard';
export function UserList() {
const { data: users, isLoading } = useUsers();
if (isLoading) return <Spinner />;
return (
<div className="grid gap-4">
{users?.map((user) => (
<UserCard key={user.id} user={user} />
))}
</div>
);
}// src/app/routes/dashboard/page.tsx
import { UserList } from '@/features/users/components/UserList';
import { PostList } from '@/features/posts/components/PostList';
import { ActivityFeed } from '@/features/activity/components/ActivityFeed';
export function DashboardPage() {
return (
<div className="grid grid-cols-3 gap-6">
<UserList />
<PostList />
<ActivityFeed />
</div>
);
}// src/features/users/index.ts
export * from './components/UserList'; // Breaks tree-shaking in Vite
export * from './api/getUsers';import { UserList } from '@/features/users/components/UserList';
import { useUsers } from '@/features/users/api/getUsers';// src/features/posts/components/PostCard.tsx
import { UserAvatar } from '@/features/users/components/UserAvatar'; // Bad// Move UserAvatar to src/components/UserAvatar.tsx
import { UserAvatar } from '@/components/UserAvatar'; // Good// Bad: Polluting shared components
src/components/
├── UserCard.tsx # Only used by users feature
├── PostCard.tsx # Only used by posts feature
└── Button.tsx # Actually shared
// Good: Feature-scoped components
src/features/users/components/UserCard.tsx
src/features/posts/components/PostCard.tsx
src/components/Button.tsxsrc/
├── app/ # Next.js App Router (routes)
│ ├── (auth)/ # Route group
│ │ ├── login/
│ │ └── register/
│ ├── dashboard/
│ ├── layout.tsx
│ └── page.tsx
├── components/ # Shared components
├── features/ # Feature modules (non-routing code)
│ ├── auth/
│ ├── dashboard/
│ └── users/
├── lib/
└── ...// src/app/users/page.tsx
import { UserList } from '@/features/users/components/UserList';
export default function UsersPage() {
return <UserList />;
}@/src/src/features/[name]/