project-structure

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.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "project-structure" with this command: npx skills add flpbalada/my-opencode-config/flpbalada-my-opencode-config-project-structure

Project Structure: Feature-Based Architecture

Core Principle

Organize code by feature/domain, not by file type. Enforce unidirectional code flow: shared → features → app.

This approach scales well for medium-to-large React, Next.js, and TypeScript projects while keeping features independent and maintainable.

Top-Level Structure

src/
├── 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 functions

Feature Structure

Each feature is a self-contained module:

src/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

Only include folders you need. Don't create empty folders "just in case."

Unidirectional Code Flow

┌─────────┐     ┌──────────┐     ┌─────┐
│ shared  │ ──► │ features │ ──► │ app │
└─────────┘     └──────────┘     └─────┘
FromCan Import From
appfeatures, shared (components, hooks, lib, types, utils)
featuresshared only
sharedOther shared modules only

Features cannot import from each other. Compose features at the app level.

Decision Guide: Where Does This Code Go?

Code TypeLocationExample
Route/page componentapp/routes/app/routes/users/page.tsx
Feature-specific componentfeatures/[name]/components/features/users/components/UserCard.tsx
Reusable UI componentcomponents/components/Button.tsx
Feature API callsfeatures/[name]/api/features/users/api/getUsers.ts
Shared utilityutils/utils/formatDate.ts
Feature utilityfeatures/[name]/utils/features/users/utils/validateUser.ts
Global statestores/stores/authStore.ts
Feature statefeatures/[name]/stores/features/users/stores/userFilterStore.ts
Library wrapperlib/lib/axios.ts, lib/dayjs.ts
Global typestypes/types/api.ts
Feature typesfeatures/[name]/types/features/users/types/user.ts

ESLint Enforcement

Prevent Cross-Feature Imports

// .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
        ],
      },
    ],
  },
};

Enforce Unidirectional Flow

// .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'],
          },
        ],
      },
    ],
  },
};

Common Patterns

Feature API with React Query

// 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,
  });
};

Feature Component

// 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>
  );
}

Composing Features at App Level

// 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>
  );
}

Anti-Patterns to Avoid

Don't Use Barrel Files (index.ts)

// src/features/users/index.ts
export * from './components/UserList';  // Breaks tree-shaking in Vite
export * from './api/getUsers';

Instead, import directly:

import { UserList } from '@/features/users/components/UserList';
import { useUsers } from '@/features/users/api/getUsers';

Don't Import Across Features

// src/features/posts/components/PostCard.tsx
import { UserAvatar } from '@/features/users/components/UserAvatar';  // Bad

Instead, lift shared components:

// Move UserAvatar to src/components/UserAvatar.tsx
import { UserAvatar } from '@/components/UserAvatar';  // Good

Don't Put Everything in Shared

If a component is only used by one feature, keep it in that feature:

// 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.tsx

Next.js App Router Adaptation

For Next.js with App Router, adapt the structure:

src/
├── 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/
└── ...

Keep route handlers minimal - delegate to feature modules:

// src/app/users/page.tsx
import { UserList } from '@/features/users/components/UserList';

export default function UsersPage() {
  return <UserList />;
}

Quick Checklist

Starting a New Project

  • Create top-level folder structure
  • Set up path aliases (@/ for src/)
  • Configure ESLint import restrictions
  • Create first feature as a template

Adding a New Feature

  • Create src/features/[name]/ directory
  • Add only needed subfolders (api, components, hooks, etc.)
  • Add ESLint zone restriction for the feature
  • Keep feature isolated - no cross-feature imports

Before Code Review

  • No cross-feature imports
  • Shared code is in shared folders
  • Feature-specific code stays in features
  • No barrel files (index.ts re-exports)

References

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

trust-psychology

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

social-proof-psychology

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

five-whys

No summary provided by upstream source.

Repository SourceNeeds Review