
Ui Component Patterns
- 237 installs
- 40 repo stars
- Updated August 4, 2026
- akillness/oh-my-skills
Implement reusable UI component patterns with consistent props, states, accessibility, and composition when building React or web interfaces in a product codebase.
About
Guides developers through proven UI component patterns for modern web apps: defining prop contracts, loading and error states, compound components, and accessible markup so interfaces stay consistent as features scale.
- Reusable component APIs
- Accessibility-first patterns
- State and variant conventions
- Composition over duplication
- Design-to-code alignment
Ui Component Patterns by the numbers
- 237 all-time installs (skills.sh)
- Ranked #814 of 2,245 Frontend Development skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/akillness/oh-my-skills --skill ui-component-patternsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 237 |
|---|---|
| repo stars | ★ 40 |
| Last updated | August 4, 2026 |
| Repository | akillness/oh-my-skills ↗ |
What it does
Implement reusable UI component patterns with consistent props, states, accessibility, and composition when building React or web interfaces in a product codebase.
Files
UI Component Patterns
Use this skill when the job is to classify the reusable component problem, choose one primary component packet, and leave behind a short component-architecture brief instead of a giant React-pattern dump.
The job is not to catalog every possible component pattern. The job is to: 1. identify the reusable component pressure first, 2. choose one bounded packet, 3. keep shared-vs-local and parent-vs-component ownership visible, 4. separate alternate-root composition from broader system or accessibility work, 5. route neighboring frontend concerns honestly.
Read these support docs first:
- references/intake-packets-and-route-outs.md
- references/component-api-checklist.md
- references/handoff-boundaries.md
When to use this skill
- A team has too many similar buttons, cards, dialogs, field wrappers, or nav patterns and needs one shared primitive or component-family boundary.
- A request mixes variants, slots, controlled-vs-uncontrolled behavior, wrappers, and docs expectations and needs routing before implementation.
- You need to decide whether something should stay product-local, become a shared primitive, or split into primitive plus wrapper.
- A component API is growing escape hatches and someone needs to shrink it into one honest packet.
- The tricky part is alternate-root composition (
buttonvsa/routerLink, slotted child, custom wrapper) rather than pure styling. - Storybook/examples exist, but the docs/verification packet for states, usage rules, or edge cases is still unclear.
When not to use this skill
- The main task is token governance, visual-language rules, primitive naming policy, or cross-product system direction →
design-system - The main task is keyboard/focus behavior, semantics, labels, announcements, contrast, or manual accessibility remediation →
web-accessibility - The main task is viewport/container adaptation, breakpoint strategy, reflow, or responsive media →
responsive-design - The main task is broader app/workflow state ownership, server/client boundaries, or cross-screen state coordination →
state-management - The main task is rerender churn, hydration, route performance, or bundle/runtime behavior →
react-best-practices - The reusable component boundary is already settled and the real job is just implementing it; in that case implement directly instead of reopening the architecture decision
Instructions
Step 1: Frame the reusable component job before naming props
Capture the minimum intake packet first.
component_intake:
surface: button-action | form-field | dialog-overlay | nav-menu | card-list-item | table-toolbar | mixed | unknown
workflow_type: new-primitive | duplicate-cleanup | api-review | wrapper-split | docs-verification | unknown
primary_packet: primitive-boundary | slot-anatomy | controlled-ownership | alternate-root-composition | docs-verification | mixed | unknown
ownership_pressure: shared-vs-local | parent-state | wrapper-proliferation | type-safety | docs-drift | mixed | unknown
signal_source: repeated-components | bug-report | code-review | storybook-gap | design-review | migration | mixed | unknown
confidence: high | medium | lowRule: do not start with “add a prop.” First label the reusable component pressure.
Step 2: Choose exactly one primary component packet
Use the router in references/intake-packets-and-route-outs.md.
Primary packets: 1. primitive-boundary 2. slot-anatomy 3. controlled-ownership 4. alternate-root-composition 5. docs-verification
Pick the packet that resolves the current decision. List anything else as follow-up, not as equal co-owners.
Step 3: Keep the invariants visible
These rules survive every serious answer:
- shared components should solve repeated work, not invent a local framework for one screen
- product-specific copy, analytics, workflow rules, and layout quirks usually belong in wrappers, not the primitive
- variants should express stable meaning (
tone,size,density,state) rather than one-off screen exceptions - slots/subcomponents are for structured flexibility, not for hiding a confused API
- if both controlled and uncontrolled behavior are offered, the contract must be explicit and coherent
- alternate-root composition changes semantics and accessibility responsibility; keep that visible
- Storybook/examples are evidence surfaces, not proof that the component boundary is healthy
Step 4: Build the component packet
Return this structure:
# Component Architecture Packet
## Scope
- Surface:
- Workflow type:
- Primary packet:
- Confidence: high | medium | low
## Current signal
- Main symptom:
- Ownership pressure:
- What is already known:
- What still needs direct verification:
## Recommended first slice
1. ...
2. ...
3. ...
## Component decisions
- Shared vs local:
- Primitive / wrapper split:
- Variants or slots:
- Controlled vs uncontrolled:
- Alternate-root or wrapper rules:
## Docs and verification
- Stories/examples required:
- State / variant matrix:
- Accessibility follow-up:
- Responsive follow-up:
## Ownership and route-outs
- Primary owner:
- Adjacent skills / teams:Step 5: Use the packet, not a giant pattern gallery
Pull the packet from references/intake-packets-and-route-outs.md.
Packet rules:
primitive-boundary→ decide shared vs local scope, primitive vs wrapper split, and what absolutely stays outside the shared APIslot-anatomy→ define subcomponents/slots, variant boundaries, composition rules, and anatomy expectations for reusable structurecontrolled-ownership→ decide whether parent state, workflow state, or component-local defaults own the lifecyclealternate-root-composition→ handleasChild/component/ wrapper-root behavior, prop spreading, refs, and semantic constraints when the primitive must compose onto another elementdocs-verification→ define Storybook/examples, state matrix, anti-usage notes, and the smallest credible verification surface for the component family
Step 6: Keep mechanism choice separate from ownership choice
Use this split in serious answers:
- Mechanism — variants, slots, subcomponents, controlled props, polymorphic root, wrapper split, docs matrix
- Ownership —
ui-component-patterns,design-system,web-accessibility,responsive-design,state-management, orreact-best-practices
If the request starts from screenshots, Storybook drift, or a code diff, say explicitly that those artifacts are the signal, not the finished component decision.
Step 7: Route adjacent work explicitly
Use these route-outs when the problem crosses boundaries:
- token governance, primitive naming, contribution policy, or cross-product component standards →
design-system - keyboard/focus remediation, label/announcement issues, ARIA, contrast, reduced motion, or manual a11y verification →
web-accessibility - viewport/container adaptation, reflow, responsive media, or dense-data layout strategy →
responsive-design - app/workflow state ownership, server/client state boundaries, URL/form/global-store decisions →
state-management - rerender churn, hydration, bundle/runtime performance, or route-level performance behavior →
react-best-practices
When mixed requests appear, keep ui-component-patterns on the reusable component packet and name the routed follow-up explicitly.
Step 8: Prefer honest boundaries over over-generalized props
When a component keeps accumulating booleans, escape hatches, or product-specific rules: 1. check whether the API should split into primitive plus wrapper, 2. collapse accidental variants into a smaller semantic set, 3. move layout- or workflow-specific behavior out of the primitive, 4. route systemic or remediation-heavy follow-ups outward instead of widening the skill.
Examples
Example 1: Button family drift
Input: “We have five button implementations and people keep adding one-off props.”
Good output direction:
- choose
primitive-boundaryas the primary packet - keep one shared action primitive plus a narrower icon/button-group wrapper if needed
- collapse random booleans into a small variant/tone/size set
- keep analytics and screen-specific layout wrappers outside the primitive
- route token naming to
design-system
Example 2: Modal state confusion
Input: “Should our modal manage its own state or always be controlled by the parent workflow?”
Good output direction:
- choose
controlled-ownershipas the primary packet - classify the modal as a reusable composite pattern
- recommend controlled ownership when workflow state, routing, or async submit logic matters
- allow an uncontrolled convenience path only if the contract remains explicit
- route keyboard/focus remediation details to
web-accessibility
Example 3: Link-button composition
Input: “Our design-system button sometimes needs to render as a router link. How should we support that without breaking types or semantics?”
Good output direction:
- choose
alternate-root-compositionas the primary packet - make prop spreading, ref forwarding, and semantic/accessibility constraints explicit
- keep route-level navigation policy or token governance outside the primitive decision
- leave behind a bounded wrapper/root contract instead of a vague “just add an
asprop” answer
Example 4: Storybook drift
Input: “The component exists, but teams keep using it differently and Storybook is missing the edge cases.”
Good output direction:
- choose
docs-verificationas the primary packet - define the minimum state/variant matrix and example set
- add anti-usage notes and wrapper expectations
- route accessibility-only or responsive-only follow-up packets outward
Best practices
1. Prefer the smallest reusable primitive that removes repeated work without inventing a local framework. 2. Keep packet choice explicit; one component decision should not silently absorb governance, remediation, responsive strategy, and runtime tuning. 3. Treat slots and subcomponents as structured API tools, not as a way to dodge ownership decisions. 4. Make controlled vs uncontrolled behavior an explicit contract, not an accidental half-state hybrid. 5. Keep alternate-root composition honest about refs, prop spreading, and semantics. 6. Use Storybook/examples as part of the evidence surface, but still write down the docs/verification packet. 7. Revisit the abstraction when consumers repeatedly need escape hatches or app-specific wrappers to use it.
References
{
"skill_name": "ui-component-patterns",
"evals": [
{
"id": 1,
"prompt": "We have three button components and people keep adding one-off props. Help us design a reusable button API.",
"expected_output": "Keeps ownership inside ui-component-patterns, chooses a primitive-boundary packet, proposes a reusable primitive with constrained variants/slots, and avoids prop explosion.",
"assertions": [
"Output classifies the task as reusable component or primitive API work",
"Output chooses a primary packet or equivalent bounded ownership",
"Output discusses variants, slots, or API shape",
"Output warns against too many one-off booleans or escape hatches"
]
},
{
"id": 2,
"prompt": "Should our modal stay self-managed or become controlled by the parent workflow?",
"expected_output": "Explains controlled-vs-uncontrolled ownership tradeoffs for a reusable component, chooses a controlled-ownership packet, and preserves component-architecture ownership inside ui-component-patterns.",
"assertions": [
"Output covers controlled vs uncontrolled behavior",
"Output keeps the task in ui-component-patterns rather than routing it away immediately",
"Output distinguishes reusable component API ownership from app-level state management"
]
},
{
"id": 3,
"prompt": "Our design-system button sometimes needs to render as a router link. How should we support that without breaking types or semantics?",
"expected_output": "Chooses an alternate-root-composition packet, makes ref forwarding / prop spreading / semantic constraints explicit, and avoids collapsing the answer into generic styling advice.",
"assertions": [
"Output covers alternate-root, asChild, component, wrapper-root, or equivalent composition behavior",
"Output mentions semantics, accessibility responsibility, refs, or prop spreading",
"Output keeps the work in ui-component-patterns unless accessibility remediation becomes primary"
]
},
{
"id": 4,
"prompt": "Our design tokens and primitive naming are inconsistent across the whole app suite. Is ui-component-patterns the right skill?",
"expected_output": "Routes system-wide token and governance work to design-system instead of over-triggering ui-component-patterns.",
"assertions": [
"Output routes system-wide token or governance work to design-system",
"Output explains that ui-component-patterns is narrower than full system governance"
]
},
{
"id": 5,
"prompt": "This dropdown is inaccessible with keyboard and screen readers. Should we fix it here or elsewhere?",
"expected_output": "Distinguishes reusable component API architecture from accessibility remediation and routes the remediation-heavy portion to web-accessibility.",
"assertions": [
"Output distinguishes component API architecture from accessibility remediation",
"Output routes accessibility-heavy work to web-accessibility",
"Output preserves ui-component-patterns for reusable primitive/API concerns if relevant"
]
},
{
"id": 6,
"prompt": "The component already exists, but Storybook is missing our loading, empty, and link-wrapper cases. What should we do?",
"expected_output": "Chooses a docs-verification packet, defines a minimum state/example matrix, and keeps Storybook/examples as a bounded evidence packet instead of reopening the entire design system.",
"assertions": [
"Output mentions Storybook, examples, states, variants, or a docs/verification matrix",
"Output keeps ownership on ui-component-patterns rather than broad design-system governance",
"Output proposes a bounded verification or example packet"
]
}
]
}
Component API Checklist for ui-component-patterns
Use this checklist before introducing a new shared primitive or refactoring a duplicated component family.
1. Reuse threshold
- Does this interaction appear in multiple places already?
- Is recurrence likely enough to justify a shared abstraction soon?
- If not, would a local component be cleaner for now?
2. Component role
Classify the candidate before designing props:
- Primitive
- Composite pattern
- Product-local wrapper
- Headless behavior layer
- Styled wrapper
If more than one role is present, split the boundary.
3. API shape
- Which props are truly required?
- Which differences are stable variants versus one-off overrides?
- Which parts belong in slots/children instead of many booleans?
- Which DOM props are safe to pass through intentionally?
4. Ownership
- Controlled, uncontrolled, or both?
- If both, is the controlled model explicit and coherent?
- Is parent ownership required for routing, async workflow, or coordination?
5. Behavioral scope
- What behavior stays inside the primitive?
- What belongs in a wrapper or domain-specific layer?
- Are analytics, copy, business rules, or layout wrappers leaking into the shared API?
6. Verification
- Which variants and states need examples?
- Which loading, error, empty, disabled, or selected states must be documented?
- Is Storybook/example coverage needed?
- What should be handed to
web-accessibilityfor remediation details? - What should be handed to
responsive-designfor layout adaptation details?
Handoff Boundaries for ui-component-patterns
Use this file when multiple frontend skills could plausibly activate.
Keep work in ui-component-patterns when
- the main job is designing or refactoring a reusable primitive or component family
- the user asks about variants, slots, controlled-vs-uncontrolled APIs, compound components, or headless/styled splits
- the problem is deciding what should become shared versus remain product-local
Route to design-system when
- the task is about system-wide tokens, primitive naming, contribution governance, or visual-language rules
- multiple component families need a shared system policy before one API can be finalized
Route to web-accessibility when
- the main problem is keyboard/focus behavior, labels, semantic HTML, ARIA, contrast, reduced motion, or manual accessibility verification
- accessibility is the core remediation task rather than one constraint among many
Route to responsive-design when
- the main problem is layout adaptation across viewports or containers
- the user primarily needs breakpoint, container-query, responsive media, or mobile-layout guidance
Route to state-management when
- the real question is app/workflow state ownership rather than component API ergonomics
- client/server state boundaries or cross-page ownership dominate the problem
Route to react-best-practices when
- the main concern is hydration, rendering performance, waterfalls, rerender behavior, or Next.js performance architecture
- the component API is already acceptable and performance is the actual bottleneck
Mixed cases
When a request spans multiple skills, split the answer explicitly:
ui-component-patternsowns reusable primitive/API architecture- neighboring skills own accessibility remediation, responsive strategy, broader system governance, or runtime performance
Do not force one skill to own every frontend concern if the better answer is a divided plan.
UI Component Intake Packets and Route-Outs
Use this file after the intake is classified. The goal is to pick one primary reusable-component packet and keep adjacent frontend work outside the main answer unless it is truly primary.
Packet router
| Packet | Use when | First decisions | Common route-outs |
|---|---|---|---|
primitive-boundary | The team is unsure whether the thing should become shared, stay local, or split into primitive plus wrapper | shared vs local, primitive vs wrapper, allowed variants, what stays out of the primitive | design-system for cross-product primitive policy; responsive-design if layout behavior is the real blocker |
slot-anatomy | The main confusion is subcomponents, slots, compound structure, or variant anatomy | slots/subcomponents, variant boundaries, composition rules, product-local escape hatches | design-system for system-wide anatomy standards; web-accessibility when semantics/focus rules become primary |
controlled-ownership | Parent workflow state and component-local defaults are fighting each other | controlled vs uncontrolled contract, default props, callback ownership, wrapper responsibilities | state-management if broader app/workflow state is the real issue |
alternate-root-composition | The component must render onto another element or wrapper (button vs link, asChild, component, custom trigger) | prop spreading, ref forwarding, semantic safety, wrapper/root contract | web-accessibility for remediation-heavy semantic/focus follow-up; design-system for shared cross-product conventions |
docs-verification | The component exists, but teams keep using it inconsistently or docs/examples are thin | minimum story matrix, anti-usage notes, wrapper examples, verification expectations | web-accessibility or responsive-design if the main missing proof is a11y or layout-specific |
Selection rules
1. Pick the packet that answers the current decision, not every downstream concern. 2. If both shared-boundary and ownership-state questions exist, choose whichever is blocking implementation now and list the other as follow-up. 3. If the only honest answer is “our stories or screenshots prove too little,” choose docs-verification. 4. If the real problem is token governance, accessibility remediation, responsive layout strategy, or runtime performance, route out early instead of hiding it under component language.
Invariants
- Shared components should remove repeated work, not encode one screen's quirks.
- Variants should stay semantic and small; repeated escape hatches usually mean the boundary is wrong.
- Wrapper/product logic belongs outside the primitive unless the primitive truly owns that responsibility.
- Alternate-root composition must keep refs, prop spreading, and semantics visible.
- Storybook/examples are evidence surfaces, not proof that the API boundary is healthy.
Output reminder
A good answer ends with one short packet:
- primary packet
- first slice
- component decisions
- docs/verification
- route-outs
N:ui-component-patterns
D:Routing-first reusable component architecture for shared frontend primitives, slots, controlled ownership, alternate-root composition, and docs/verification packets. Use when the user needs to decide what should become a shared primitive, how a component API should expose variants or subcomponents, when parent state should control the component, how to compose onto alternate element types or wrappers, or what Storybook/example coverage is required.
G:ui-components react typescript composition variants slots reusable frontend
U[5]:
Choose what should become a shared primitive versus stay product-local
Design component APIs with variants, slots, or subcomponents without prop sprawl
Decide controlled-vs-uncontrolled ownership for reusable components
Handle alternate-root composition for buttons, links, dialogs, or wrappers
Define the docs/verification packet for stories, examples, and edge cases
S[5]{n,action}:
1,Frame the reusable component job before naming props
2,Choose one primary component packet
3,Keep the invariants visible
4,Build the component architecture packet
5,Route adjacent frontend work honestly