When this skill is active, follow this 6-step discipline when designing React components:
1. Decompose Before Building
Sketch the component tree before writing JSX:
- Identify the smallest reusable pieces — a component should do one thing
- Name components after what they represent, not what they render:
UserAvatar, not CircleImage
- Separate container components (data fetching, state) from presentational components (rendering, styling)
- Co-locate related files:
UserCard.tsx, UserCard.test.tsx, UserCard.module.css
2. Define the Component Contract
Design the props interface before implementing the component:
- Export the props type:
export interface UserCardProps { ... }
- Use discriminated unions for variant props:
{ variant: 'compact' } | { variant: 'full'; bio: string }
- Require only what the component needs — prefer many small props over one config object
- Use
children for composition, renderX props for slot patterns, as prop for polymorphism
3. Assign State Ownership
Decide where each piece of state lives before writing hooks:
- Inventory every piece of state the feature needs — list them all before placing any
- Categorize each one: UI-local (open/closed), shared (lifted), server-fetched, or URL-persisted
- Draw the data flow: which component owns each state, which receive it as props, which dispatch updates
- Identify derived state: if a value can be computed from other state, compute it — don't store it separately
- Rule: if two components need the same state, lift it — don't sync with
useEffect
4. Build Accessibility In
Accessibility is part of the design, not an afterthought:
- Use semantic HTML:
<button>, <nav>, <main>, <dialog> — not <div onClick>
- Add ARIA attributes only when semantic HTML is insufficient:
aria-label, aria-expanded, role
- Support keyboard navigation: focus management,
tabIndex, onKeyDown for custom interactions
- Test with a screen reader or
axe-core: npm run test -- --reporter=axe
5. Handle All UI States
Every data-driven component renders four states:
- Loading: skeleton or spinner — never a blank screen
- Empty: helpful message with a call to action
- Error: actionable message with a retry option — show what went wrong
- Success: the actual content with proper transitions
- Use error boundaries (
ErrorBoundary) to catch rendering failures without crashing the app
6. Test the Component Contract
Write tests against the public API, not internal implementation:
- Render with different prop combinations — especially edge cases and discriminated union variants
- Simulate user interactions: click, type, submit — use
@testing-library/user-event
- Assert on visible output: text content, ARIA roles, element presence — never test internal state
- Test accessibility:
expect(screen.getByRole('button')).toBeInTheDocument()
- Run with
npx vitest or npx jest — zero failures
- If any test fails, fix the issue and re-run the entire suite
Converted and distributed by TomeVault — claim your Tome and manage your conversions.
1---2name: react-design3description: React component design process. Covers decomposition, state ownership, accessibility, and contract testing. Use when this capability is needed.4---56When this skill is active, follow this 6-step discipline when designing React components:78## 1. Decompose Before Building910Sketch the component tree before writing JSX:11- Identify the smallest reusable pieces — a component should do one thing12- Name components after what they represent, not what they render: `UserAvatar`, not `CircleImage`13- Separate **container components** (data fetching, state) from **presentational components** (rendering, styling)14- Co-locate related files: `UserCard.tsx`, `UserCard.test.tsx`, `UserCard.module.css`1516## 2. Define the Component Contract1718Design the props interface before implementing the component:19- Export the props type: `export interface UserCardProps { ... }`20- Use discriminated unions for variant props: `{ variant: 'compact' } | { variant: 'full'; bio: string }`21- Require only what the component needs — prefer many small props over one config object22- Use `children` for composition, `renderX` props for slot patterns, `as` prop for polymorphism2324## 3. Assign State Ownership2526Decide where each piece of state lives before writing hooks:27- **Inventory every piece of state** the feature needs — list them all before placing any28- **Categorize each one**: UI-local (open/closed), shared (lifted), server-fetched, or URL-persisted29- **Draw the data flow**: which component owns each state, which receive it as props, which dispatch updates30- **Identify derived state**: if a value can be computed from other state, compute it — don't store it separately31- Rule: if two components need the same state, lift it — don't sync with `useEffect`3233## 4. Build Accessibility In3435Accessibility is part of the design, not an afterthought:36- Use semantic HTML: `<button>`, `<nav>`, `<main>`, `<dialog>` — not `<div onClick>`37- Add ARIA attributes only when semantic HTML is insufficient: `aria-label`, `aria-expanded`, `role`38- Support keyboard navigation: focus management, `tabIndex`, `onKeyDown` for custom interactions39- Test with a screen reader or `axe-core`: `npm run test -- --reporter=axe`4041## 5. Handle All UI States4243Every data-driven component renders four states:44- **Loading**: skeleton or spinner — never a blank screen45- **Empty**: helpful message with a call to action46- **Error**: actionable message with a retry option — show what went wrong47- **Success**: the actual content with proper transitions48- Use error boundaries (`ErrorBoundary`) to catch rendering failures without crashing the app4950## 6. Test the Component Contract5152Write tests against the public API, not internal implementation:53- Render with different prop combinations — especially edge cases and discriminated union variants54- Simulate user interactions: click, type, submit — use `@testing-library/user-event`55- Assert on visible output: text content, ARIA roles, element presence — never test internal state56- Test accessibility: `expect(screen.getByRole('button')).toBeInTheDocument()`57- Run with `npx vitest` or `npx jest` — zero failures58- If any test fails, fix the issue and re-run the entire suite5960---61> Converted and distributed by [TomeVault](https://tomevault.io/claim/fricklers) — claim your Tome and manage your conversions.62<!-- tomevault:4.0:skill_md:2026-04-15 -->