GitHub

Component Anatomy

The consistent internal structure every component follows — imports, types, constants, function, export.

01

Principle

When every component follows the same structure, you stop thinking about where things go inside a file and start thinking about what the component actually does. Consistent anatomy means anyone on the team can open any file and immediately know where to look — props are always at the top, constants are always before the function, exports are always at the bottom.

lightbulb

The hardest part of component anatomy is constants vs. props. Rule of thumb: if it never changes based on what's passed in, it is a constant. If it could change from outside, it is a prop.

02

Rules

  • check_circle
    Imports firstOrder: React → external libraries → internal aliases (@/) → relative imports (./). This makes dependencies visible at a glance.
  • check_circle
    Types and interfaces secondDefine all types used in this file immediately after imports. Props interface always comes first.
  • check_circle
    Constants thirdComponent-scoped constants (static data, config, labels) come before the function. Never define constants inside the function body.
  • check_circle
    Component function fourthThe function itself comes after everything it depends on. Keep it focused — if it grows past 200 lines, split it.
  • check_circle
    Named export lastAlways use named exports, never default exports. Named exports make refactoring and search easier.
03

Pattern

components/RecipeCard.tsx — anatomy template
// 1. IMPORTS — React → external → internal → relative
import { useState } from 'react';
import Link from 'next/link';
import { cn } from '@/shared/utils/cn';
import { useSavedStore } from '../stores/useSavedStore';

// 2. TYPES
interface RecipeCardProps {
  slug: string;
  title: string;
  description: string;
  category: string;
}

// 3. CONSTANTS — static, never changes based on props
const MAX_DESCRIPTION_LENGTH = 120;
const CARD_BASE_CLASS = 'rounded-xl border bg-white shadow-sm';

// 4. COMPONENT FUNCTION
export function RecipeCard({ slug, title, description, category }: RecipeCardProps) {
  const [isHovered, setIsHovered] = useState(false);
  const { isSaved } = useSavedStore();

  const truncated = description.slice(0, MAX_DESCRIPTION_LENGTH);

  return (
    <div className={cn(CARD_BASE_CLASS, isHovered && 'shadow-lg')}>
      {/* ... */}
    </div>
  );
}

// 5. EXPORT — named, at the bottom
// (already exported above with "export function")
04

Implementation

info

Version Compatibility

Requires React 19+ and the latest stable versions of all dependencies shown.

In Next.js, mark client components explicitly with 'use client' — it goes above all imports as the very first line.

features/cookbook/components/RecipeCard.tsx
// 'use client' goes ABOVE imports if the component is a Client Component
'use client';

// 1. IMPORTS
import { useState } from 'react';
import Link from 'next/link';
import { cn } from '@/shared/utils/cn';

// 2. TYPES
interface RecipeCardProps {
  slug: string;
  title: string;
  framework: 'nextjs' | 'vitejs';
}

// 3. CONSTANTS
const HREF_MAP = {
  nextjs: '/nextjs/cookbook',
  vitejs: '/vitejs/cookbook',
} as const;

// 4. COMPONENT
export function RecipeCard({ slug, title, framework }: RecipeCardProps) {
  const [saved, setSaved] = useState(false);
  const href = `${HREF_MAP[framework]}/${slug}`;

  return (
    <Link href={href}>
      <span>{title}</span>
    </Link>
  );
}
menu_book
React Patterns

Helping developers build robust React applications since 2025.

© 2025 React Patterns Cookbook. Built with ❤️ for the community.
react-principles