# React Component Structure

> Best practices for structuring React components - function declarations, file organization, and Single Responsibility Principle Use when this capability is needed.

- Skill: `tomevault-io/react-component-structure` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add tomevault-io/react-component-structure`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tomevault-io/react-component-structure/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- Author: tomevault-io (https://skillmd.com/u/tomevault-io)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/tomevault-io/react-component-structure

---


# React Component Structure

Best practices for structuring React components for readability, testability, and maintainability.

---

## Function Declarations vs Const

**Always use function declarations** for components, not const with arrow functions.

### ❌ Don't

```tsx
const UserProfile = ({ name, email }: Props) => {
  return (
    <div>
      <h1>{name}</h1>
      <p>{email}</p>
    </div>
  )
}

// Or worse
const UserProfile: React.FC<Props> = ({ name, email }) => {
  return (
    <div>
      <h1>{name}</h1>
      <p>{email}</p>
    </div>
  )
}
```

### ✅ Do

```tsx
function UserProfile({ name, email }: Props) {
  return (
    <div>
      <h1>{name}</h1>
      <p>{email}</p>
    </div>
  );
}
```

### Why?

1. **Better stack traces**: Function declarations show the component name clearly in error stacks
2. **Hoisting**: Can reference components before they're defined in the file
3. **Avoid React.FC pitfalls**:
   - Implicitly includes `children` (often unwanted)
   - Return type is `ReactNode` which includes `undefined` (allows accidental `undefined` returns)
   - Incompatible with some generics patterns
   - Adds unnecessary abstraction
4. **Clearer intent**: Function declarations signal "this is a component"
5. **TypeScript inference**: Props type is explicit and clear

---

## Single Responsibility Principle

Components should do **one thing well**. Avoid mixing concerns or creating "god components".

> 📖 **For comprehensive SOLID principles including SRP, see:** [code-standards/rules/solid-principles.md](../code-standards/rules/solid-principles.md#s---single-responsibility-principle)

**In React context, SRP means:**

- Each component has one clear purpose
- Easy to name descriptively
- Easy to test in isolation
- Changes for one reason only

### ❌ Don't: Multiple helpers before component

```tsx
// File: UserProfile.tsx
function formatDate(date: Date): string {
  return new Intl.DateTimeFormat('nl-NL').format(date);
}

function calculateAge(birthDate: Date): number {
  const today = new Date();
  const age = today.getFullYear() - birthDate.getFullYear();
  return age;
}

function validateEmail(email: string): boolean {
  return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}

function formatPhoneNumber(phone: string): string {
  return phone.replace(/(\d{3})(\d{3})(\d{4})/, '($1) $2-$3');
}

function capitalizeWords(str: string): string {
  return str.replace(/\b\w/g, (l) => l.toUpperCase());
}

// Finally, the component (after 5 helpers!)
function UserProfile({ user }: Props) {
  return (
    <div>
      <h1>{capitalizeWords(user.name)}</h1>
      <p>Age: {calculateAge(user.birthDate)}</p>
      <p>Email: {user.email}</p>
      <p>Phone: {formatPhoneNumber(user.phone)}</p>
    </div>
  );
}
```

### ✅ Do: Extract helpers to utility files

```tsx
// File: UserProfile.tsx
import { calculateAge, formatDate } from '@/utils/date-helpers';
import { formatPhoneNumber } from '@/utils/formatters';
import { capitalizeWords } from '@/utils/string-helpers';
import { validateEmail } from '@/utils/validators';

function UserProfile({ user }: Props) {
  return (
    <div>
      <h1>{capitalizeWords(user.name)}</h1>
      <p>Age: {calculateAge(user.birthDate)}</p>
      <p>Email: {user.email}</p>
      <p>Phone: {formatPhoneNumber(user.phone)}</p>
    </div>
  );
}
```

### ✅ Alternative: Place simple helpers after component

For **component-specific** helpers that are short (1-3 lines):

```tsx
// File: UserCard.tsx
function UserCard({ user, onEdit }: Props) {
  const displayName = formatDisplayName(user.firstName, user.lastName);
  const initials = getInitials(user.firstName, user.lastName);

  return (
    <div>
      <Avatar>{initials}</Avatar>
      <h2>{displayName}</h2>
      <button onClick={() => onEdit(user.id)}>Edit</button>
    </div>
  );
}

// Simple, component-specific helpers placed AFTER
function formatDisplayName(first: string, last: string): string {
  return `${first} ${last}`;
}

function getInitials(first: string, last: string): string {
  return `${first[0]}${last[0]}`.toUpperCase();
}
```

---

## Component Organization Pattern

**Recommended file structure:**

```tsx
// 1. Imports (external first, then internal)
import { formatCurrency } from '@/utils/formatters';
import { useEffect, useState } from 'react';

// 2. Constants (if needed)
const DISCOUNT_THRESHOLD = 100;

// 3. Types/Interfaces (as close as possible to component)
interface ProductCardProps {
  product: Product;
  onAddToCart: (id: string) => void;
}

// 4. Main component
function ProductCard({ product, onAddToCart }: ProductCardProps) {
  const [quantity, setQuantity] = useState(1);
  const discountedPrice = calculateDiscount(product.price, quantity);

  return (
    <div>
      <h3>{product.name}</h3>
      <p>{formatCurrency(discountedPrice)}</p>
      <button onClick={() => onAddToCart(product.id)}>Add to Cart</button>
    </div>
  );
}

// 5. Component-specific helpers (if simple and tightly coupled)
function calculateDiscount(price: number, qty: number): number {
  return qty >= DISCOUNT_THRESHOLD ? price * 0.9 : price;
}

// 6. Export
export { ProductCard };
```

---

## When to Extract

### Extract to separate file when:

- ✅ Helper is reused across 2+ components
- ✅ Logic is complex (>10 lines)
- ✅ Helper is domain logic (business rules)
- ✅ Helper needs separate testing

### Keep in component file when:

- ✅ Used only in this component
- ✅ Simple (1-5 lines)
- ✅ Tightly coupled to component logic
- ✅ Pure formatting/transformation

---

## Anti-Patterns

### ❌ Don't: God component

```tsx
function Dashboard() {
  // 300 lines of logic handling:
  // - User authentication
  // - Data fetching
  // - Filtering
  // - Sorting
  // - Pagination
  // - Charts rendering
  // - Form handling
  return <>{/* 200 lines of JSX */}</>;
}
```

### ✅ Do: Compose smaller components

```tsx
function Dashboard() {
  return (
    <DashboardLayout>
      <UserHeader />
      <FilterControls />
      <DataTable />
      <ChartSection />
    </DashboardLayout>
  );
}
```

### ❌ Don't: Mixing presentation and logic

```tsx
function ProductList() {
  // Data fetching, filtering, sorting, pagination
  const [products, setProducts] = useState([]);
  const [filters, setFilters] = useState({});
  // ... 50 lines of logic

  return <>{/* Presentation */}</>;
}
```

### ✅ Do: Separate concerns

```tsx
function ProductList() {
  const { products, isLoading } = useProducts(); // Custom hook for logic

  if (isLoading) {
    return <LoadingSpinner />;
  }

  return <ProductGrid products={products} />;
}
```

---

## Summary

| Aspect                | Guideline                                                  |
| --------------------- | ---------------------------------------------------------- |
| Component syntax      | Function declarations, not const                           |
| React.FC              | Avoid (unnecessary, has pitfalls)                          |
| Helpers               | Extract to utils if reused or complex                      |
| Component size        | Keep focused (~50-100 lines)                               |
| Organization          | Imports → Constants → Props → Component → Helpers → Export |
| Props location        | Define immediately before component                        |
| Single Responsibility | One component, one purpose                                 |

**Principle:** Components should be easy to understand at a glance. If you have to scroll past 5 helpers to find the component, refactor.

---
> Converted and distributed by [TomeVault](https://tomevault.io/claim/alexandrebenkendorf) — claim your Tome and manage your conversions.
<!-- tomevault:4.0:skill_md:2026-04-13 -->

