react-component-architecture
Scannednpx machina-cli add skill autohandai/community-skills/react-component-architecture --openclawReact Component Architecture
Component Design Principles
- Single Responsibility - Each component does one thing well
- Composition Over Configuration - Use children and render props over prop drilling
- Colocation - Keep related code together (styles, tests, types)
- Controlled vs Uncontrolled - Be explicit about state ownership
Component Patterns
Compound Components
For complex UI with shared state:
const Tabs = ({ children, defaultValue }: TabsProps) => {
const [active, setActive] = useState(defaultValue);
return (
<TabsContext.Provider value={{ active, setActive }}>
{children}
</TabsContext.Provider>
);
};
Tabs.List = TabsList;
Tabs.Trigger = TabsTrigger;
Tabs.Content = TabsContent;
Render Props for Flexibility
When consumers need control over rendering:
interface ListProps<T> {
items: T[];
renderItem: (item: T, index: number) => ReactNode;
keyExtractor: (item: T) => string;
}
function List<T>({ items, renderItem, keyExtractor }: ListProps<T>) {
return items.map((item, i) => (
<Fragment key={keyExtractor(item)}>{renderItem(item, i)}</Fragment>
));
}
Custom Hooks for Logic Extraction
Extract reusable stateful logic:
function useToggle(initial = false) {
const [state, setState] = useState(initial);
const toggle = useCallback(() => setState(s => !s), []);
const setTrue = useCallback(() => setState(true), []);
const setFalse = useCallback(() => setState(false), []);
return { state, toggle, setTrue, setFalse } as const;
}
Polymorphic Components
Components that render as different elements:
type PolymorphicProps<E extends ElementType> = {
as?: E;
} & ComponentPropsWithoutRef<E>;
function Box<E extends ElementType = 'div'>({
as,
...props
}: PolymorphicProps<E>) {
const Component = as || 'div';
return <Component {...props} />;
}
Props Patterns
Discriminated Union Props
For mutually exclusive prop combinations:
type ButtonProps =
| { variant: 'link'; href: string; onClick?: never }
| { variant: 'button'; onClick: () => void; href?: never };
Default Props with Destructuring
function Button({
variant = 'primary',
size = 'md',
...props
}: ButtonProps) {
// ...
}
Performance Patterns
- Memoize expensive computations with
useMemo - Memoize callbacks passed to children with
useCallback - Split contexts by update frequency
- Use
React.memofor pure presentational components - Virtualize long lists with react-virtual or similar
File Structure
components/
Button/
Button.tsx # Component
Button.test.tsx # Tests
Button.types.ts # Types (if complex)
index.ts # Re-export
Source
git clone https://github.com/autohandai/community-skills/blob/main/react-component-architecture/SKILL.mdView on GitHub Overview
Adopt scalable React UI design using patterns like compound components, render props, hooks, and polymorphic components, all in TypeScript. It emphasizes single responsibility, composition over configuration, and colocating related code (styles, tests, and types) for maintainable components.
How This Skill Works
Technically, compounds use context to share state across subcomponents, and render props and custom hooks extract reusable logic. Polymorphic components support rendering as different elements via an as prop; discriminated unions enforce safe, mutually exclusive props; and each component's types, tests, and styles are colocated in its folder. Performance improvements come from memoization and splitting contexts where needed.
When to Use It
- You need shared state across parts of a complex UI (e.g., Tabs) and want clean boundaries via compound components.
- When consumers require control over rendering and layout (use render props or function as children).
- You're building reusable, polymorphic UI elements that can render as multiple HTML tags with a single API.
- You want strong type safety for prop combinations using discriminated unions.
- You aim to optimize performance in large UIs by memoizing expensive work and splitting contexts.
Quick Start
- Step 1: Plan components and state boundaries with SRP; decide on shared state via context if needed.
- Step 2: Implement compound components, render props, and a custom hook to extract logic; add polymorphic as prop where appropriate.
- Step 3: Add TS types, tests, and organize files (types, tests, styles) in a dedicated component folder; apply memoization and context splitting as needed.
Best Practices
- Start with Single Responsibility for every component.
- Prefer composition over prop drilling using children or render props.
- Colocate related code: keep styles, tests, and types alongside the component.
- Use discriminated unions for clear, type-safe prop shapes.
- Memoize expensive computations and callbacks; split contexts to minimize updates.
Example Use Cases
- Tabs implemented with compound components sharing state via context.
- Generic List component using renderItem for flexible rendering.
- Polymorphic Box component with an as prop for different element types.
- Button component using discriminated unions to switch between link and button behavior.
- useToggle custom hook to extract reusable toggle logic.