D

Dynamic Component Composition Patterns Studio

Enterprise-ready skill that automates reusable React component composition and patterns. Built for Claude Code with best practices and real-world patterns.

SkillCommunityfrontendv1.0.0MIT
0 views0 copies

Component Composition Patterns Studio

Advanced React component composition toolkit covering compound components, render props, headless UI patterns, slot-based composition, and provider patterns for building flexible, reusable component libraries.

When to Use This Skill

Choose Component Composition Patterns when:

  • Building reusable component libraries or design systems
  • Creating flexible components that work across many use cases
  • Refactoring rigid components into composable primitives
  • Implementing headless UI components with custom styling
  • Designing API surfaces that balance flexibility and simplicity

Consider alternatives when:

  • Building one-off components — keep it simple, don't over-abstract
  • Using a pre-built library — use their existing composition patterns
  • Need performance over flexibility — prefer concrete components

Quick Start

# Activate composition patterns claude skill activate dynamic-component-composition-patterns-studio # Refactor to compound components claude "Refactor the Select component to use compound component pattern" # Build headless component claude "Create a headless Accordion component with full keyboard support"

Example: Compound Component Pattern

// Compound Components — flexible, declarative API import { createContext, useContext, useState, useCallback, ReactNode } from 'react'; // Internal context interface AccordionContextValue { openItems: Set<string>; toggle: (id: string) => void; multiple: boolean; } const AccordionContext = createContext<AccordionContextValue | null>(null); function useAccordion() { const ctx = useContext(AccordionContext); if (!ctx) throw new Error('Accordion components must be used within <Accordion>'); return ctx; } // Root component interface AccordionProps { children: ReactNode; multiple?: boolean; defaultOpen?: string[]; } function Accordion({ children, multiple = false, defaultOpen = [] }: AccordionProps) { const [openItems, setOpenItems] = useState(new Set(defaultOpen)); const toggle = useCallback((id: string) => { setOpenItems(prev => { const next = new Set(multiple ? prev : []); if (prev.has(id)) next.delete(id); else next.add(id); return next; }); }, [multiple]); return ( <AccordionContext.Provider value={{ openItems, toggle, multiple }}> <div role="region">{children}</div> </AccordionContext.Provider> ); } // Child components function AccordionItem({ id, children }: { id: string; children: ReactNode }) { return <div data-accordion-item={id}>{children}</div>; } function AccordionTrigger({ id, children }: { id: string; children: ReactNode }) { const { openItems, toggle } = useAccordion(); const isOpen = openItems.has(id); return ( <button onClick={() => toggle(id)} aria-expanded={isOpen} aria-controls={`panel-${id}`}> {children} <ChevronIcon className={isOpen ? 'rotate-180' : ''} /> </button> ); } function AccordionContent({ id, children }: { id: string; children: ReactNode }) { const { openItems } = useAccordion(); if (!openItems.has(id)) return null; return <div id={`panel-${id}`} role="region">{children}</div>; } // Compose the API Accordion.Item = AccordionItem; Accordion.Trigger = AccordionTrigger; Accordion.Content = AccordionContent; // Usage — clean, declarative, flexible <Accordion multiple defaultOpen={['faq-1']}> <Accordion.Item id="faq-1"> <Accordion.Trigger id="faq-1">What is this?</Accordion.Trigger> <Accordion.Content id="faq-1"> <p>This is a compound component accordion.</p> </Accordion.Content> </Accordion.Item> <Accordion.Item id="faq-2"> <Accordion.Trigger id="faq-2">How does it work?</Accordion.Trigger> <Accordion.Content id="faq-2"> <p>State is shared via context between compound children.</p> </Accordion.Content> </Accordion.Item> </Accordion>

Core Concepts

Composition Patterns

PatternDescriptionBest For
Compound ComponentsRelated components share implicit state via contextAccordions, tabs, menus, selects
Render PropsFunction-as-child passing render control to consumerData fetching, animations, state logic
Headless ComponentsLogic hooks without UI, consumer provides markupCustom-styled design systems
Slot PatternNamed insertion points for content customizationLayouts, cards, dialogs
Provider PatternContext-based state sharing across treeTheme, auth, feature flags
Higher-Order ComponentsFunctions that wrap components with behaviorCross-cutting concerns (deprecated in favor of hooks)

Pattern Selection Guide

NeedRecommended PatternWhy
Multiple related components that share stateCompound ComponentsDeclarative, clean API
Reusable logic with custom UIHeadless (hooks)Maximum flexibility
Flexible rendering within a componentRender PropsConsumer controls output
Consistent layout with customizable sectionsSlot PatternStructured flexibility
Global state (theme, auth)Provider PatternTree-wide access
// Headless Component Pattern — logic hook function useToggle(initial = false) { const [isOn, setIsOn] = useState(initial); const toggle = useCallback(() => setIsOn(prev => !prev), []); const setOn = useCallback(() => setIsOn(true), []); const setOff = useCallback(() => setIsOn(false), []); return { isOn, toggle, setOn, setOff }; } // Consumer provides all UI function FeatureFlag({ featureName }: { featureName: string }) { const { isOn, toggle } = useToggle(false); return ( <div className="flex items-center gap-2"> <span>{featureName}</span> <button onClick={toggle} className={isOn ? 'bg-green-500' : 'bg-gray-300'} role="switch" aria-checked={isOn} > {isOn ? 'ON' : 'OFF'} </button> </div> ); }

Configuration

ParameterDescriptionDefault
patternDefault pattern: compound, headless, render-propcompound
typescriptFull TypeScript support with genericstrue
accessibilityInclude ARIA attributes and keyboard supporttrue
animationInclude animation support hooksfalse
testingGenerate testing utilities alongside componentstrue

Best Practices

  1. Start with the consumer API, then implement internally — Write example usage code first to design the ideal developer experience, then implement the internals to match. This produces components that are intuitive to use rather than easy to build.

  2. Use compound components for related UI elements that share state — When multiple components need to communicate (tabs + panels, select + options, accordion + items), compound components with shared context create the cleanest API.

  3. Provide both headless hooks and pre-styled components — Export a useAccordion() hook for full customization and a styled <Accordion> component for quick usage. This serves both design system consumers (need flexibility) and application developers (need speed).

  4. Make components controlled and uncontrolled — Support both value + onChange (controlled) and defaultValue (uncontrolled) modes. This matches React form conventions and allows consumers to choose their state management approach.

  5. Include TypeScript generics for type-safe data binding — Components that render lists or handle data should use generics: <Select<User> items={users} getLabel={u => u.name}>. This propagates type information to event handlers and render functions.

Common Issues

Context-based compound components break when consumers add wrapper elements. Use React's Children.map and cloneElement sparingly — they're fragile. Instead, use context for state sharing so children can be nested at any depth within the provider tree.

Render prop pattern creates deeply nested "callback hell" JSX. When multiple render props stack, readability suffers. Refactor to hooks: convert <Toggle>{(isOn) => ...}</Toggle> to const { isOn } = useToggle(). Hooks compose more cleanly than nested render props.

Headless components require too much boilerplate for simple use cases. Provide pre-composed "batteries-included" variants alongside the headless primitives. Export <Accordion> (styled, pre-composed) and useAccordion() (headless, flexible) from the same package to serve both quick and custom use cases.

Community

Reviews

Write a review

No reviews yet. Be the first to review this template!

Similar Templates