Skip to content
component-organization logo

Component Organization Guide

component-organization

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

SKILL.md

Full skill instructions

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."

More skills from majiayu000

xiaohongshu logo
majiayu000/claude-arsenal

xiaohongshu

xiaohongshu

286 148
View
agent-task-conductor logo
majiayu000/claude-skill-registry

agent-task-conductor

Conduct multi-agent task orchestration and workflow coordination.

663 1
View
conductor-setup logo
majiayu000/claude-skill-registry

conductor-setup

Initialize project with Conductor artifacts (product definition,

663 1
View
animation-designer logo
majiayu000/claude-skill-registry

animation-designer

Expert in web animations, transitions, and motion design using Framer Motion and CSS

663 1
View
diagramming logo
majiayu000/claude-skill-registry

diagramming

Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...

663 1
View
h3-pg logo
majiayu000/claude-skill-registry-data

h3-pg

PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.

23 1
View
conductor-development logo
majiayu000/claude-skill-registry

conductor-development

Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...

663 1
View
conductor-status logo
majiayu000/claude-skill-registry

conductor-status

Display project status, active tracks, and next actions

663 1
View
dockerization logo
majiayu000/claude-skill-registry

dockerization

Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.

663 1
View

Popular AI tools

Kaiber logo
Video

Kaiber

Generate, edit, and beat-sync AI video with leading models in one workspace.

Paid
View
Vimcal logo
Productivity

Vimcal

The world's fastest calendar for remote work

Free
View

Transform Your Design with AI Designer by ImgCreator.ai

Freemium
View
Akool AI logo
Content & writing

Akool AI

Revolutionizing Video Production with AI-Powered Creativity

Paid
View

Extend an image past the frame and let AI fill the new aspect ratio.

Freemium
View
StarByFace logo
Security

StarByFace

Discover your celebrity doppelgänger with StarByFace!

Free
View
C

ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.

Freemium
View
Opus Clip logo
Coding & apps

Opus Clip

Opus.ai: Revolutionize Your Web Experience

Free
View