Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Use this skill when you’re building or refactoring React components in a production app and want **consistent patterns** for typing, composition, state, and performance without introducing premature abstraction.
.claude/skills/amariahak-react-component-patterns/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | -9% | 0% |
| case-19 | ✗→✓ | ▲ Improved | 18% | 0% |
| case-09 | ✓→✗ | ▼ Worse | -28% | 0% |
| case-18 | ✓→✓ | = Same ✓ | 21% | 0% |
| case-03 | ✓→✓ | = Same ✓ | 29% | 0% |
Use this skill when you’re building or refactoring React components in a production app and want consistent patterns for typing, composition, state, and performance without introducing premature abstraction.
...rest unless you’re building a wrapper that truly forwards).Example:
tsxexport interface ButtonProps { variant?: "primary" | "secondary"; disabled?: boolean; onClick?: () => void; children: React.ReactNode; } export function Button({ variant = "primary", disabled, onClick, children }: ButtonProps) { return ( <button data-variant={variant} disabled={disabled} onClick={onClick}> {children} </button> ); }
type)interface for public component props (extends well, readable in IDEs).type for unions, helpers, and composition.Patterns:
interface XProps { ... }type Variant = "a" | "b"type Props = XProps & { ... } when combining.Controlled inputs make state explicit and testable.
Pattern:
value + onChange(next).tsxexport interface TextFieldProps { value: string; onChange: (next: string) => void; label?: string; } export function TextField({ value, onChange, label }: TextFieldProps) { return ( <label> {label} <input value={value} onChange={(e) => onChange(e.target.value)} /> </label> ); }
For components/Foo/:
Foo.tsx (component)Foo.test.tsx (tests)Foo.stories.tsx (storybook if used)types.ts (shared types) only if it growsAvoid:
index.ts barrels that hide importsRules:
use.[value, setValue]).tsexport function useDisclosure(initial = false) { const [open, setOpen] = React.useState(initial); return { open, openNow: () => setOpen(true), close: () => setOpen(false), toggle: () => setOpen((v) => !v), }; }
Most apps do not need useMemo/useCallback everywhere.
Use memoization when:
useEffect / subscriptionsAvoid memoization when:
memo, useMemo, useCallback: when they helpGuideline:
Common anti-pattern:
useCallback “just in case”Use React Context for:
Avoid Context for:
If you need app-wide, high-frequency state:
Options in order:
Pattern:
<Menu> provides context<Menu.Button> and <Menu.Items> consume itBenefits:
Render props are useful when you want to expose internal state without forcing component structure. Prefer hooks first; use render props when you need to interleave layout and control.
grep/glob to find component usage patterns; use read_file to inspect existing props and state flow.edit_file, then run tests.grep/glob to locate entry points and affected components, then read them with read_file.run_command (Build mode) to run vitest/playwright or the project’s test scripts.When reviewing a component, check:
useMemo/useCallback everywhere “for performance” without measuringany in props; prefer precise unions and unknown where needed| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-18 | pass→pass | 9,456 | 5,207 | -45% | 1 | 1 | 0% | 1,996 | 2,411 | +21% | 0 | 0 | — |
case-01 | fail→pass | 21,765 | 9,613 | -56% | 1 | 1 | 0% | 3,685 | 3,371 | -9% | 0 | 0 | — |
case-02 | fail→fail | 20,012 | 16,608 | -17% | 1 | 1 | 0% | 4,547 | 4,091 | -10% | 0 | 0 | — |
case-03 | pass→pass | 11,368 | 7,446 | -35% | 1 | 1 | 0% | 2,124 | 2,735 | +29% | 0 | 0 | — |
case-04 | pass→pass | 7,047 | 5,086 | -28% | 1 | 1 | 0% | 1,441 | 2,435 | +69% | 0 | 0 | — |
case-05 | pass→pass | 15,461 | 10,864 | -30% | 1 | 1 | 0% | 2,974 | 3,441 | +16% | 0 | 0 | — |
case-06 | pass→pass | 13,343 | 6,094 | -54% | 1 | 1 | 0% | 2,545 | 2,409 | -5% | 0 | 0 | — |
case-07 | pass→pass | 12,520 | 12,506 | -0% | 1 | 1 | 0% | 2,474 | 3,788 | +53% | 0 | 0 | — |
case-08 | pass→pass | 20,546 | 18,274 | -11% | 1 | 1 | 0% | 4,300 | 4,316 | +0% | 0 | 0 | — |
case-09 | pass→fail | 12,529 | 5,035 | -60% | 1 | 1 | 0% | 2,270 | 1,639 | -28% | 0 | 0 | — |
case-10 | pass→pass | 8,485 | 4,000 | -53% | 1 | 1 | 0% | 1,621 | 2,104 | +30% | 0 | 0 | — |
case-11 | pass→pass | 11,063 | 3,934 | -64% | 1 | 1 | 0% | 1,354 | 2,075 | +53% | 0 | 0 | — |
case-12 | pass→pass | 7,027 | 9,679 | +38% | 1 | 1 | 0% | 1,395 | 3,611 | +159% | 0 | 0 | — |
case-13 | pass→pass | 10,048 | 9,112 | -9% | 1 | 1 | 0% | 1,760 | 2,444 | +39% | 0 | 0 | — |
case-14 | pass→pass | 15,845 | 9,537 | -40% | 1 | 1 | 0% | 2,446 | 2,809 | +15% | 0 | 0 | — |
case-15 | pass→pass | 10,259 | 3,577 | -65% | 1 | 1 | 0% | 1,690 | 1,946 | +15% | 0 | 0 | — |
case-16 | pass→pass | 14,256 | 9,676 | -32% | 1 | 1 | 0% | 2,515 | 3,048 | +21% | 0 | 0 | — |
case-17 | pass→pass | 13,158 | 9,569 | -27% | 1 | 1 | 0% | 2,279 | 2,985 | +31% | 0 | 0 | — |
case-19 | fail→pass | 9,909 | 5,432 | -45% | 1 | 1 | 0% | 2,074 | 2,456 | +18% | 0 | 0 | — |
case-20 | pass→pass | 5,599 | 3,922 | -30% | 1 | 1 | 0% | 1,014 | 2,078 | +105% | 0 | 0 | — |
case-21 | pass→pass | 7,884 | 5,580 | -29% | 1 | 1 | 0% | 1,563 | 2,483 | +59% | 0 | 0 | — |
case-22 | pass→pass | 10,906 | 6,744 | -38% | 1 | 1 | 0% | 2,291 | 2,777 | +21% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +5 percentage points is the difference between those two pass rates over the 22 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.