Claude Code Plugins

Community-maintained marketplace

Feedback

component-organization

@JewelsHovan/pain-plus-site
0
0

Maintain consistent file structure - components/ui for base shadcn, components/shared for composed components, pages for routes, always use barrel exports

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name component-organization
description Maintain consistent file structure - components/ui for base shadcn, components/shared for composed components, pages for routes, always use barrel exports

Component Organization Guide

This guide establishes the file structure and naming conventions for maintaining a consistent, scalable React TypeScript codebase.

Directory Structure

src/
├── assets/                  # Static assets (images, icons, fonts)
│   ├── icons/
│   └── images/
├── components/              # Reusable components
│   ├── ui/                  # Base shadcn components (CLI-generated)
│   │   ├── button.tsx
│   │   ├── card.tsx
│   │   ├── badge.tsx
│   │   └── index.ts         # Barrel export
│   ├── shared/              # Composed app-specific components
│   │   ├── UserProfileCard.tsx
│   │   ├── DataTable.tsx
│   │   └── index.ts         # Barrel export
│   └── index.ts             # Root barrel export
├── pages/                   # Top-level route components
│   ├── Home.tsx
│   ├── About.tsx
│   ├── NotFound.tsx
│   └── index.ts             # Barrel export
├── layouts/                 # Layout wrappers
│   ├── MainLayout.tsx
│   ├── DashboardLayout.tsx
│   └── index.ts             # Barrel export
├── features/                # Feature-based organization (optional)
│   ├── authentication/
│   │   ├── components/
│   │   ├── hooks/
│   │   └── index.ts
│   └── dashboard/
│       ├── components/
│       ├── hooks/
│       └── index.ts
├── hooks/                   # Custom React hooks
│   ├── useLocalStorage.ts
│   └── index.ts             # Barrel export
├── lib/                     # Utilities and helpers
│   └── utils.ts             # cn() helper for Tailwind
├── services/                # API and external services
│   ├── api.service.ts
│   └── index.ts             # Barrel export
├── constants/               # App-wide constants
│   └── index.ts             # APP_NAME, ROUTES, etc.
├── types/                   # TypeScript type definitions
│   ├── api.types.ts
│   ├── common.types.ts
│   └── index.ts             # Barrel export
└── utils/                   # Pure utility functions
    └── index.ts

Directory Purposes

components/ui/

Purpose: Base shadcn/ui components generated via CLI

  • DO: Add new components using npx shadcn@latest add [component]
  • DO: Keep these components pure and unopinionated
  • DON'T: Modify these files directly (regenerate if needed)
  • DON'T: Add business logic here

Example:

// components/ui/button.tsx (generated by shadcn CLI)
import * as React from "react"
import { cn } from "@/lib/utils"

const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
  ({ className, variant, size, ...props }, ref) => {
    return (
      <button
        className={cn(buttonVariants({ variant, size, className }))}
        ref={ref}
        {...props}
      />
    )
  }
)

components/shared/

Purpose: Composed, app-specific components built from base UI components

  • DO: Create components that combine multiple ui/ components
  • DO: Add business logic and app-specific styling
  • DO: Make these components reusable across the app
  • DON'T: Use for one-off components (put those in features or pages)

Example:

// components/shared/UserProfileCard.tsx
import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';

export interface UserProfileCardProps {
  name: string;
  role: string;
  avatar: string;
}

export function UserProfileCard({ name, role, avatar }: UserProfileCardProps) {
  return (
    <Card>
      <CardHeader>
        <img src={avatar} alt={name} className="h-16 w-16 rounded-full" />
        <CardTitle>{name}</CardTitle>
        <Badge>{role}</Badge>
      </CardHeader>
      <CardContent>
        {/* Additional user details */}
      </CardContent>
    </Card>
  );
}

pages/

Purpose: Top-level route components that render at specific URLs

  • DO: Keep components focused on layout and orchestration
  • DO: Use descriptive names matching routes (Home.tsx, About.tsx)
  • DON'T: Put heavy business logic here (extract to hooks/services)

Example:

// pages/Home.tsx
import {
  Card,
  CardDescription,
  CardHeader,
  CardTitle,
} from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';
import { APP_NAME } from '@/constants';

export function Home() {
  const features = [
    {
      title: 'Vite',
      badge: 'Fast',
      description: 'Lightning-fast build tool with HMR.',
      gradient: 'from-violet-500 to-purple-500',
    },
    // ... more features
  ];

  return (
    <div className="space-y-32">
      {/* Hero Section */}
      <div className="relative overflow-hidden">
        <h1 className="text-6xl font-extrabold">{APP_NAME}</h1>
        <p className="text-xl text-gray-600">
          A modern React TypeScript starter
        </p>
      </div>

      {/* Features Grid */}
      <div className="grid gap-8 md:grid-cols-2 lg:grid-cols-3">
        {features.map((feature) => (
          <Card key={feature.title}>
            <CardHeader>
              <CardTitle>{feature.title}</CardTitle>
              <Badge>{feature.badge}</Badge>
              <CardDescription>{feature.description}</CardDescription>
            </CardHeader>
          </Card>
        ))}
      </div>
    </div>
  );
}

layouts/

Purpose: Wrapper components that provide consistent structure across pages

  • DO: Handle navigation, headers, footers, sidebars
  • DO: Use <Outlet /> from react-router for nested routing
  • DON'T: Include page-specific content

Example:

// layouts/MainLayout.tsx
import { Link, Outlet, useLocation } from 'react-router-dom';

export function MainLayout() {
  const location = useLocation();
  const isActive = (path: string) => location.pathname === path;

  return (
    <div className="min-h-screen bg-white">
      {/* Navigation Header */}
      <header className="sticky top-0 z-50 border-b bg-white/95">
        <nav className="mx-auto max-w-7xl px-8 py-4">
          <div className="flex items-center gap-8">
            <Link
              to="/"
              className={`px-4 py-2 font-medium ${
                isActive('/') ? 'text-blue-600' : 'text-gray-600'
              }`}
            >
              Home
            </Link>
            <Link
              to="/about"
              className={`px-4 py-2 font-medium ${
                isActive('/about') ? 'text-blue-600' : 'text-gray-600'
              }`}
            >
              About
            </Link>
          </div>
        </nav>
      </header>

      {/* Main Content */}
      <main className="mx-auto max-w-7xl px-8 py-16">
        <Outlet />
      </main>
    </div>
  );
}

lib/

Purpose: Core utilities and helper functions

  • DO: Keep the cn() helper for Tailwind class merging
  • DO: Add utilities that integrate external libraries
  • DON'T: Put pure business logic here (use utils/ instead)

Example:

// lib/utils.ts
import { clsx, type ClassValue } from 'clsx';
import { twMerge } from 'tailwind-merge';

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs));
}

constants/

Purpose: Application-wide constants and configuration

  • DO: Use SCREAMING_SNAKE_CASE for constant names
  • DO: Use as const for type safety
  • DON'T: Put environment variables here (use import.meta.env)

Example:

// constants/index.ts
export const APP_NAME = 'Vite React TypeScript Boilerplate';

export const API_BASE_URL =
  import.meta.env.VITE_API_BASE_URL || 'http://localhost:3000/api';

export const ROUTES = {
  HOME: '/',
  ABOUT: '/about',
  NOT_FOUND: '*',
} as const;

export const STORAGE_KEYS = {
  AUTH_TOKEN: 'auth_token',
  USER_PREFERENCES: 'user_preferences',
} as const;

Barrel Exports

Always create index.ts files to enable clean imports and maintain a clear public API.

Pattern 1: Named Exports (Recommended)

// components/ui/index.ts
export { Button } from './button';
export {
  Card,
  CardHeader,
  CardFooter,
  CardTitle,
  CardDescription,
  CardContent,
} from './card';
export { Badge } from './badge';
export { Separator } from './separator';

Pattern 2: Re-export All

// Only use when all exports from a module should be public
export * from './button';
export * from './card';

Pattern 3: Nested Barrel Exports

// components/index.ts - Exposes nested directories
export { Button } from './ui/button';
export {
  Card,
  CardHeader,
  CardFooter,
  CardTitle,
  CardDescription,
  CardContent,
} from './ui/card';

Benefits:

  • Clean imports: import { Button, Card } from '@/components'
  • Refactoring safety: Move files without breaking imports
  • Clear public API: Only exported items are "public"

Naming Conventions

Components

Format: PascalCase

  • Files: Button.tsx, UserProfileCard.tsx, MainLayout.tsx
  • Components: export function Button(), export function UserProfileCard()

Utilities and Services

Format: camelCase or kebab-case

  • Files: utils.ts, api.service.ts, useLocalStorage.ts
  • Functions: export function cn(), export function useLocalStorage()

Directories

Format: kebab-case

  • components/ui, components/shared, pages, layouts

Constants

Format: SCREAMING_SNAKE_CASE

  • APP_NAME, API_BASE_URL, STORAGE_KEYS

Types and Interfaces

Format: PascalCase with descriptive suffix

  • UserProfileCardProps, ApiResponse, AuthState

Decision Flowchart: Where to Place New Components

┌─────────────────────────────────────┐
│ Need to create a new component?    │
└──────────────┬──────────────────────┘
               │
               ▼
┌─────────────────────────────────────┐
│ Is it a shadcn/ui base component?  │
└──────────────┬──────────────────────┘
               │
         ┌─────┴─────┐
         │           │
        YES          NO
         │           │
         ▼           ▼
   ┌─────────┐  ┌──────────────────────────────────┐
   │ Use CLI │  │ Is it used in 2+ different       │
   │ to add  │  │ features/pages?                  │
   │ to ui/  │  └──────────────┬───────────────────┘
   └─────────┘                 │
                         ┌─────┴─────┐
                         │           │
                        YES          NO
                         │           │
                         ▼           ▼
              ┌──────────────────┐  ┌─────────────────────┐
              │ Does it compose  │  │ Is it a full page   │
              │ multiple ui/     │  │ route?              │
              │ components?      │  └──────────┬──────────┘
              └────────┬─────────┘             │
                       │              ┌────────┴────────┐
                 ┌─────┴─────┐        │                 │
                 │           │       YES                NO
                YES          NO        │                 │
                 │           │         ▼                 ▼
                 ▼           ▼    ┌────────┐    ┌──────────────┐
          ┌──────────┐  ┌────────┐│ pages/ │    │ Is it part   │
          │ shared/  │  │ hooks/ ││        │    │ of a cohesive│
          │          │  │ or     │└────────┘    │ feature?     │
          └──────────┘  │ utils/ │              └──────┬───────┘
                        └────────┘                     │
                                              ┌────────┴────────┐
                                              │                 │
                                             YES                NO
                                              │                 │
                                              ▼                 ▼
                                    ┌──────────────┐   ┌──────────────┐
                                    │ features/    │   │ Colocate with│
                                    │ [feature]/   │   │ parent       │
                                    │ components/  │   │ component    │
                                    └──────────────┘   └──────────────┘

When to Create New Components

DRY Principle: Reusability > 2x Usage

If you're copying the same JSX structure more than twice, extract it into a component.

Before:

// Multiple pages with duplicated card structure
export function Dashboard() {
  return (
    <Card>
      <CardHeader>
        <CardTitle>Stats</CardTitle>
      </CardHeader>
      <CardContent>{/* ... */}</CardContent>
    </Card>
  );
}

export function Profile() {
  return (
    <Card>
      <CardHeader>
        <CardTitle>User Info</CardTitle>
      </CardHeader>
      <CardContent>{/* ... */}</CardContent>
    </Card>
  );
}

After:

// components/shared/StatsCard.tsx
export function StatsCard({ title, children }) {
  return (
    <Card>
      <CardHeader>
        <CardTitle>{title}</CardTitle>
      </CardHeader>
      <CardContent>{children}</CardContent>
    </Card>
  );
}

Complexity: Component Logic > 50 Lines

If a component or section exceeds 50 lines, consider extracting it.

Before:

export function Dashboard() {
  return (
    <div>
      {/* 80 lines of user profile UI */}
      <div className="user-profile">
        {/* Complex profile rendering */}
      </div>

      {/* 60 lines of statistics UI */}
      <div className="statistics">
        {/* Complex stats rendering */}
      </div>
    </div>
  );
}

After:

export function Dashboard() {
  return (
    <div>
      <UserProfile />
      <Statistics />
    </div>
  );
}

Composition: Building from Multiple Base Components

When combining 3+ base UI components, create a composed component.

// components/shared/UserCard.tsx - Composes Card, Badge, Button
import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';

export function UserCard({ user }) {
  return (
    <Card>
      <CardHeader>
        <div className="flex items-center justify-between">
          <CardTitle>{user.name}</CardTitle>
          <Badge>{user.role}</Badge>
        </div>
      </CardHeader>
      <CardContent>
        <p>{user.email}</p>
        <Button variant="outline">View Profile</Button>
      </CardContent>
    </Card>
  );
}

Domain Logic: App-Specific Behavior

When adding business logic, API calls, or state management, extract to a component.

// components/shared/ProductList.tsx - Contains domain logic
import { useEffect, useState } from 'react';
import { fetchProducts } from '@/services/api.service';
import { Card } from '@/components/ui/card';

export function ProductList() {
  const [products, setProducts] = useState([]);

  useEffect(() => {
    fetchProducts().then(setProducts);
  }, []);

  return (
    <div className="grid gap-4">
      {products.map(product => (
        <Card key={product.id}>
          {/* Product display */}
        </Card>
      ))}
    </div>
  );
}

Real-World Examples from This Project

Example 1: UI Component Barrel Export

// src/components/ui/index.ts
export { Button } from './button';
export {
  Card,
  CardHeader,
  CardFooter,
  CardTitle,
  CardDescription,
  CardContent,
} from './card';
export { Badge } from './badge';
export { Separator } from './separator';

Example 2: Page Component with Feature Imports

// src/pages/Home.tsx
import {
  Card,
  CardDescription,
  CardHeader,
  CardTitle,
} from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';
import { APP_NAME } from '@/constants';

export function Home() {
  // Page orchestrates UI components
  return (
    <div className="space-y-32">
      <h1>{APP_NAME}</h1>
      {/* Feature showcase using composed components */}
    </div>
  );
}

Example 3: Layout with Router Integration

// src/layouts/MainLayout.tsx
import { Link, Outlet, useLocation } from 'react-router-dom';

export function MainLayout() {
  const location = useLocation();

  return (
    <div className="min-h-screen">
      <header className="sticky top-0">
        <nav>
          <Link to="/">Home</Link>
          <Link to="/about">About</Link>
        </nav>
      </header>

      <main>
        <Outlet /> {/* Pages render here */}
      </main>
    </div>
  );
}

Example 4: Constants with Type Safety

// src/constants/index.ts
export const APP_NAME = 'Vite React TypeScript Boilerplate';

export const ROUTES = {
  HOME: '/',
  ABOUT: '/about',
  NOT_FOUND: '*',
} as const;

// Usage in pages
import { ROUTES } from '@/constants';

<Link to={ROUTES.HOME}>Home</Link>

Quick Reference Checklist

  • Created barrel export index.ts for new directory?
  • Used PascalCase for component files and exports?
  • Used kebab-case for directory names?
  • Added shadcn components via CLI to components/ui/?
  • Placed composed components in components/shared/?
  • Put route components in pages/ directory?
  • Exported layouts from layouts/ directory?
  • Used SCREAMING_SNAKE_CASE for constants?
  • Added TypeScript types for component props?
  • Verified component is reused 2+ times before creating?
  • Extracted complex logic (>50 lines) to separate component?
  • Used @/ path alias for imports?

Anti-Patterns to Avoid

DON'T modify shadcn UI components directly:

// ❌ Bad: Editing ui/button.tsx manually
// If you need customization, create a wrapper in shared/

DON'T skip barrel exports:

// ❌ Bad: Direct imports without barrel
import { Button } from '@/components/ui/button';
import { Card } from '@/components/ui/card';

// ✅ Good: Use barrel exports
import { Button, Card } from '@/components/ui';

DON'T create one-off components in shared/:

// ❌ Bad: Single-use component in shared/
// components/shared/HomepageHeroSection.tsx (only used once)

// ✅ Good: Keep in the page or feature
// pages/Home.tsx (inline) or features/homepage/components/

DON'T mix concerns in constants:

// ❌ Bad: Mixing types and runtime values
export const ENDPOINTS = {
  users: '/api/users',
  auth: '/api/auth',
};
export type User = { id: string; name: string };

// ✅ Good: Separate constants and types
// constants/index.ts - runtime values
// types/index.ts - type definitions

Remember: Good organization makes scaling easy. When in doubt, follow the principle: "Start simple, extract when you repeat."