React Core Patterns
When to Use
- Creating React components
- Working with hooks (useState, useEffect, custom hooks)
- Implementing Suspense boundaries
- Setting up lazy loading with React.lazy
- Using React 19 patterns (ref as prop, no forwardRef)
- Structuring component files
- Optimizing component performance
Purpose
Essential React 19 patterns for building modern applications with hooks, Suspense, lazy loading, and TypeScript.
Note: DSAI Component Convention (React 19):
forwardRefis REQUIRED for all DSAI components — usememo(forwardRef(function Name(props, ref)))pattern- Every component MUST have a
displayNameproperty - Props go in separate
*.types.tsfiles propTypesremoved (use TypeScript interfaces)- New JSX transform required
React.FCtype discouraged — use direct function components with typed props
When to Use This Skill
- Creating React components
- Using React hooks (useState, useEffect, useCallback, useMemo)
- Implementing lazy loading and code splitting
- Working with Suspense boundaries
- React-specific TypeScript patterns
- Performance optimization with React
Quick Start
Component Structure Template
import { forwardRef, memo, useState, useCallback } from 'react';
import { cn } from '@/lib/utils';
import type { UserProfileProps } from './UserProfile.types';
// Props defined in UserProfile.types.ts:
// interface UserProfileProps {
// userId: string;
// onUpdate?: (data: UserData) => void;
// className?: string;
// }
export const UserProfile = memo(
forwardRef<HTMLDivElement, UserProfileProps>(
function UserProfile({ userId, onUpdate, className }, ref) {
const [data, setData] = useState<UserData | null>(null);
const handleUpdate = useCallback((newData: UserData) => {
setData(newData);
onUpdate?.(newData);
}, [onUpdate]);
return (
<div ref={ref} className={cn('user-profile', className)}>
{/* Component content */}
</div>
);
}
)
);
UserProfile.displayName = 'UserProfile';
Component Checklist
Creating a React component? Follow this:
- Use
memo(forwardRef(function Name(props, ref)))pattern - Set
displayNameon every component - Props in separate
*.types.tsfile - Use
cn()for class name composition (Bootstrap classes, not Tailwind) - Use
useCallbackfor event handlers passed to children - Use
useMemofor expensive computations - Lazy load if heavy component:
lazy(() => import()) - Wrap lazy components in
<Suspense>with fallback - Named export only (no default exports)
- No conditional hooks (hooks must be called in same order)
- Import from
@/components/ui/for DSAI components
Core Hooks Patterns
See hooks-patterns.md for useState, useCallback, useMemo, and useEffect patterns with TypeScript examples.
Lazy Loading & Code Splitting
See lazy-loading.md for React.lazy, Suspense fallbacks, and feature-based code splitting examples.
Suspense Patterns
Suspense Boundaries
// Wrap data-fetching components
<Suspense fallback={<Skeleton />}>
<UserProfile userId={id} />
</Suspense>
// Nested Suspense for granular loading
<Suspense fallback={<PageLoader />}>
<Header />
<Suspense fallback={<ContentSkeleton />}>
<MainContent />
</Suspense>
<Footer />
</Suspense>
Error Boundaries with Suspense
import { ErrorBoundary } from 'react-error-boundary';
<ErrorBoundary fallback={<ErrorFallback />}>
<Suspense fallback={<Loading />}>
<DataComponent />
</Suspense>
</ErrorBoundary>
TypeScript Patterns
See typescript-patterns.md for component props, hooks typing, and custom hook return types.
Performance Optimization
See performance.md for React.memo usage, custom comparison functions, and avoiding re-renders.
Common Patterns
Conditional Rendering
// Ternary operator
{isLoading ? <Spinner /> : <Content />}
// Logical AND
{error && <ErrorMessage error={error} />}
// Nullish coalescing
{user ?? <GuestView />}
// Early return for loading states
function Component() {
const { data } = useSomeHook();
// ❌ Avoid early returns for loading - breaks hooks rules
// Use Suspense instead
return <div>{data.map(...)}</div>;
}
Lists and Keys
// Always use stable keys
{items.map(item => (
<ItemCard key={item.id} item={item} />
))}
// Never use index as key if list can reorder
// ❌ Bad
{items.map((item, index) => (
<ItemCard key={index} item={item} />
))}
File Organization
Feature-Based Structure
src/client/
├── components/
│ ├── ui/ # DSAI components (installed via `dsai add`)
│ │ ├── button/
│ │ │ ├── Button.tsx
│ │ │ ├── Button.types.ts
│ │ │ ├── Button.fsm.ts
│ │ │ └── index.ts
│ │ ├── modal/
│ │ └── card/
│ └── features/ # App-specific feature components
│ ├── auth/
│ └── posts/
├── hooks/ # Shared hooks (DSAI + custom)
├── lib/
│ └── utils/ # Utilities (cn, validators, etc.)
└── types/ # Shared types
Component Co-location
components/ui/button/
├── Button.tsx # Component with forwardRef + displayName
├── Button.types.ts # TypeScript prop interfaces
├── Button.fsm.ts # FSM reducer (interactive components)
├── Button.test.tsx # Unit tests (Jest 30 + RTL)
├── Button.a11y.test.tsx # Accessibility tests (jest-axe)
└── index.ts # Barrel exports
Common Mistakes to Avoid
See common-mistakes.md for conditional hooks, missing dependencies, and state mutation anti-patterns.
Additional Resources
For more detailed patterns, see:
- component-patterns.md - Advanced component patterns
- performance.md - Performance optimization techniques
- typescript-patterns.md - TypeScript best practices
- hooks-patterns.md - Custom hooks and advanced patterns