# Component Patterns Reference ## Compound Components Deep Dive Compound components share implicit state while allowing flexible composition. ### Implementation with Context ```tsx import { createContext, useContext, useState, useCallback, type ReactNode, type Dispatch, type SetStateAction, } from 'react'; // Types interface TabsContextValue { activeTab: string; setActiveTab: Dispatch>; } interface TabsProps { defaultValue: string; children: ReactNode; onChange?: (value: string) => void; } interface TabListProps { children: ReactNode; className?: string; } interface TabProps { value: string; children: ReactNode; disabled?: boolean; } interface TabPanelProps { value: string; children: ReactNode; } // Context const TabsContext = createContext(null); function useTabs() { const context = useContext(TabsContext); if (!context) { throw new Error('Tabs components must be used within '); } return context; } // Root Component export function Tabs({ defaultValue, children, onChange }: TabsProps) { const [activeTab, setActiveTab] = useState(defaultValue); const handleChange: Dispatch> = useCallback( (value) => { const newValue = typeof value === 'function' ? value(activeTab) : value; setActiveTab(newValue); onChange?.(newValue); }, [activeTab, onChange] ); return (
{children}
); } // Tab List (container for tab triggers) Tabs.List = function TabList({ children, className }: TabListProps) { return (
{children}
); }; // Individual Tab (trigger) Tabs.Tab = function Tab({ value, children, disabled }: TabProps) { const { activeTab, setActiveTab } = useTabs(); const isActive = activeTab === value; return ( ); }; // Tab Panel (content) Tabs.Panel = function TabPanel({ value, children }: TabPanelProps) { const { activeTab } = useTabs(); if (activeTab !== value) return null; return (
{children}
); }; ``` ### Usage ```tsx Overview Features Pricing

Product Overview

Description here...

Key Features

    ...
``` ## Render Props Pattern Delegate rendering control to the consumer while providing state and helpers. ```tsx interface DataLoaderRenderProps { data: T | null; loading: boolean; error: Error | null; refetch: () => void; } interface DataLoaderProps { url: string; children: (props: DataLoaderRenderProps) => ReactNode; } function DataLoader({ url, children }: DataLoaderProps) { const [state, setState] = useState<{ data: T | null; loading: boolean; error: Error | null; }>({ data: null, loading: true, error: null, }); const fetchData = useCallback(async () => { setState(prev => ({ ...prev, loading: true, error: null })); try { const response = await fetch(url); if (!response.ok) throw new Error('Fetch failed'); const data = await response.json(); setState({ data, loading: false, error: null }); } catch (error) { setState(prev => ({ ...prev, loading: false, error: error as Error })); } }, [url]); useEffect(() => { fetchData(); }, [fetchData]); return <>{children({ ...state, refetch: fetchData })}; } // Usage url="/api/users"> {({ data, loading, error, refetch }) => { if (loading) return ; if (error) return ; return ; }} ``` ## Polymorphic Components Components that can render as different HTML elements. ```tsx type AsProp = { as?: C; }; type PropsToOmit = keyof (AsProp & P); type PolymorphicComponentProp< C extends React.ElementType, Props = {} > = React.PropsWithChildren> & Omit, PropsToOmit>; interface TextOwnProps { variant?: 'body' | 'heading' | 'label'; size?: 'sm' | 'md' | 'lg'; } type TextProps = PolymorphicComponentProp; function Text({ as, variant = 'body', size = 'md', className, children, ...props }: TextProps) { const Component = as || 'span'; const variantClasses = { body: 'font-normal', heading: 'font-bold', label: 'font-medium uppercase tracking-wide', }; const sizeClasses = { sm: 'text-sm', md: 'text-base', lg: 'text-lg', }; return ( {children} ); } // Usage Default span Paragraph Heading Label ``` ## Controlled vs Uncontrolled Pattern Support both modes for maximum flexibility. ```tsx interface InputProps { // Controlled value?: string; onChange?: (value: string) => void; // Uncontrolled defaultValue?: string; // Common placeholder?: string; disabled?: boolean; } function Input({ value: controlledValue, onChange, defaultValue = '', ...props }: InputProps) { const [internalValue, setInternalValue] = useState(defaultValue); // Determine if controlled const isControlled = controlledValue !== undefined; const value = isControlled ? controlledValue : internalValue; const handleChange = (e: React.ChangeEvent) => { const newValue = e.target.value; if (!isControlled) { setInternalValue(newValue); } onChange?.(newValue); }; return ( ); } // Controlled usage const [search, setSearch] = useState(''); // Uncontrolled usage ``` ## Slot Pattern Allow consumers to replace internal parts. ```tsx interface CardProps { children: ReactNode; header?: ReactNode; footer?: ReactNode; media?: ReactNode; } function Card({ children, header, footer, media }: CardProps) { return (
{media && (
{media}
)} {header && (
{header}
)}
{children}
{footer && (
{footer}
)}
); } // Usage with slots } header={

Card Title

} footer={} >

Card content goes here.

``` ## Forward Ref Pattern Allow parent components to access the underlying DOM node. ```tsx import { forwardRef, useRef, useImperativeHandle } from 'react'; interface InputHandle { focus: () => void; clear: () => void; getValue: () => string; } interface FancyInputProps { label: string; placeholder?: string; } const FancyInput = forwardRef( ({ label, placeholder }, ref) => { const inputRef = useRef(null); useImperativeHandle(ref, () => ({ focus: () => inputRef.current?.focus(), clear: () => { if (inputRef.current) inputRef.current.value = ''; }, getValue: () => inputRef.current?.value ?? '', })); return (
); } ); FancyInput.displayName = 'FancyInput'; // Usage function Form() { const inputRef = useRef(null); const handleSubmit = () => { console.log(inputRef.current?.getValue()); inputRef.current?.clear(); }; return (
); } ```