From react-toolkit
Implements React patterns like composition, custom hooks, render props, HOCs, and compound components for reusable, maintainable UIs and refactoring.
How this skill is triggered — by the user, by Claude, or both
Slash command
/react-toolkit:component-patternsThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Implement React component patterns for building reusable, maintainable components.
Implement React component patterns for building reusable, maintainable components.
Use composition for most cases, custom hooks for shared logic, and compound components for flexible APIs.
The default pattern for building flexible components.
Basic composition:
function Card({ children }) {
return <div className="card">{children}</div>;
}
function CardHeader({ children }) {
return <div className="card-header">{children}</div>;
}
function CardBody({ children }) {
return <div className="card-body">{children}</div>;
}
// Usage
<Card>
<CardHeader>Title</CardHeader>
<CardBody>Content</CardBody>
</Card>
With props:
interface ButtonProps {
variant?: 'primary' | 'secondary';
size?: 'sm' | 'md' | 'lg';
children: React.ReactNode;
}
function Button({ variant = 'primary', size = 'md', children }: ButtonProps) {
return (
<button className={`btn btn-${variant} btn-${size}`}>
{children}
</button>
);
}
Extract and reuse stateful logic across components.
Basic custom hook:
function useToggle(initialValue = false) {
const [value, setValue] = useState(initialValue);
const toggle = useCallback(() => {
setValue(v => !v);
}, []);
return [value, toggle] as const;
}
// Usage
function Component() {
const [isOpen, toggleOpen] = useToggle();
return <button onClick={toggleOpen}>{isOpen ? 'Close' : 'Open'}</button>;
}
Data fetching hook:
function useFetch<T>(url: string) {
const [data, setData] = useState<T | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
fetch(url)
.then(res => res.json())
.then(setData)
.catch(setError)
.finally(() => setLoading(false));
}, [url]);
return { data, loading, error };
}
Form hook:
function useForm<T>(initialValues: T) {
const [values, setValues] = useState(initialValues);
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setValues(prev => ({
...prev,
[e.target.name]: e.target.value
}));
};
const reset = () => setValues(initialValues);
return { values, handleChange, reset };
}
Create flexible component APIs with implicit state sharing.
Basic compound component:
interface TabsContextValue {
activeTab: string;
setActiveTab: (tab: string) => void;
}
const TabsContext = createContext<TabsContextValue | null>(null);
function Tabs({ children, defaultTab }: { children: React.ReactNode; defaultTab: string }) {
const [activeTab, setActiveTab] = useState(defaultTab);
return (
<TabsContext.Provider value={{ activeTab, setActiveTab }}>
<div className="tabs">{children}</div>
</TabsContext.Provider>
);
}
function TabList({ children }: { children: React.ReactNode }) {
return <div className="tab-list">{children}</div>;
}
function Tab({ id, children }: { id: string; children: React.ReactNode }) {
const context = useContext(TabsContext);
if (!context) throw new Error('Tab must be used within Tabs');
const { activeTab, setActiveTab } = context;
return (
<button
className={activeTab === id ? 'active' : ''}
onClick={() => setActiveTab(id)}
>
{children}
</button>
);
}
function TabPanel({ id, children }: { id: string; children: React.ReactNode }) {
const context = useContext(TabsContext);
if (!context) throw new Error('TabPanel must be used within Tabs');
if (context.activeTab !== id) return null;
return <div className="tab-panel">{children}</div>;
}
// Attach sub-components
Tabs.List = TabList;
Tabs.Tab = Tab;
Tabs.Panel = TabPanel;
// Usage
<Tabs defaultTab="home">
<Tabs.List>
<Tabs.Tab id="home">Home</Tabs.Tab>
<Tabs.Tab id="profile">Profile</Tabs.Tab>
</Tabs.List>
<Tabs.Panel id="home">Home content</Tabs.Panel>
<Tabs.Panel id="profile">Profile content</Tabs.Panel>
</Tabs>
Provide render flexibility through function props.
Basic render prop:
interface MouseTrackerProps {
render: (position: { x: number; y: number }) => React.ReactNode;
}
function MouseTracker({ render }: MouseTrackerProps) {
const [position, setPosition] = useState({ x: 0, y: 0 });
useEffect(() => {
const handleMove = (e: MouseEvent) => {
setPosition({ x: e.clientX, y: e.clientY });
};
window.addEventListener('mousemove', handleMove);
return () => window.removeEventListener('mousemove', handleMove);
}, []);
return <>{render(position)}</>;
}
// Usage
<MouseTracker
render={({ x, y }) => (
<div>Mouse at {x}, {y}</div>
)}
/>
Children as function:
interface DataProviderProps<T> {
url: string;
children: (data: T | null, loading: boolean) => React.ReactNode;
}
function DataProvider<T>({ url, children }: DataProviderProps<T>) {
const { data, loading } = useFetch<T>(url);
return <>{children(data, loading)}</>;
}
// Usage
<DataProvider url="/api/users">
{(users, loading) => (
loading ? <Spinner /> : <UserList users={users} />
)}
</DataProvider>
Wrap components to add functionality (legacy pattern, prefer hooks).
Basic HOC:
function withLoading<P extends object>(
Component: React.ComponentType<P>
) {
return function WithLoadingComponent(props: P & { loading: boolean }) {
const { loading, ...rest } = props;
if (loading) return <Spinner />;
return <Component {...(rest as P)} />;
};
}
// Usage
const UserListWithLoading = withLoading(UserList);
<UserListWithLoading users={users} loading={loading} />
HOC with additional props:
function withAuth<P extends object>(
Component: React.ComponentType<P & { user: User }>
) {
return function WithAuthComponent(props: P) {
const { user, loading } = useAuth();
if (loading) return <Spinner />;
if (!user) return <Navigate to="/login" />;
return <Component {...props} user={user} />;
};
}
Prevent unnecessary re-renders of expensive components.
const ExpensiveComponent = React.memo(function ExpensiveComponent({ data }) {
// Expensive rendering logic
return <div>{/* Complex UI */}</div>;
});
// With custom comparison
const MemoizedComponent = React.memo(
function Component({ user }) {
return <div>{user.name}</div>;
},
(prevProps, nextProps) => prevProps.user.id === nextProps.user.id
);
Memoize expensive calculations.
function Component({ items }) {
const sortedItems = useMemo(() => {
return items.sort((a, b) => a.value - b.value);
}, [items]);
const total = useMemo(() => {
return items.reduce((sum, item) => sum + item.price, 0);
}, [items]);
return <div>{/* Use sortedItems and total */}</div>;
}
Memoize functions to prevent child re-renders.
function Parent() {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
setCount(c => c + 1);
}, []);
return <MemoizedChild onClick={handleClick} />;
}
const MemoizedChild = React.memo(function Child({ onClick }) {
return <button onClick={onClick}>Click</button>;
});
Split code to reduce initial bundle size.
const LazyComponent = React.lazy(() => import('./HeavyComponent'));
function App() {
return (
<Suspense fallback={<Spinner />}>
<LazyComponent />
</Suspense>
);
}
// Route-based splitting
const Dashboard = React.lazy(() => import('./pages/Dashboard'));
const Profile = React.lazy(() => import('./pages/Profile'));
<Routes>
<Route path="/dashboard" element={
<Suspense fallback={<Spinner />}>
<Dashboard />
</Suspense>
} />
</Routes>
Handle large lists efficiently.
import { useVirtualizer } from '@tanstack/react-virtual';
function VirtualList({ items }) {
const parentRef = useRef<HTMLDivElement>(null);
const virtualizer = useVirtualizer({
count: items.length,
getScrollElement: () => parentRef.current,
estimateSize: () => 50,
});
return (
<div ref={parentRef} style={{ height: '400px', overflow: 'auto' }}>
<div style={{ height: `${virtualizer.getTotalSize()}px`, position: 'relative' }}>
{virtualizer.getVirtualItems().map(virtualItem => (
<div
key={virtualItem.key}
style={{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: `${virtualItem.size}px`,
transform: `translateY(${virtualItem.start}px)`,
}}
>
{items[virtualItem.index].name}
</div>
))}
</div>
</div>
);
}
Separate logic from presentation.
// Presentational component
interface UserListProps {
users: User[];
onDelete: (id: string) => void;
}
function UserList({ users, onDelete }: UserListProps) {
return (
<ul>
{users.map(user => (
<li key={user.id}>
{user.name}
<button onClick={() => onDelete(user.id)}>Delete</button>
</li>
))}
</ul>
);
}
// Container component
function UserListContainer() {
const { data: users, isLoading } = useQuery(['users'], fetchUsers);
const deleteMutation = useMutation(deleteUser);
if (isLoading) return <Spinner />;
return <UserList users={users} onDelete={deleteMutation.mutate} />;
}
Share data across component tree.
interface ThemeContextValue {
theme: 'light' | 'dark';
toggleTheme: () => void;
}
const ThemeContext = createContext<ThemeContextValue | null>(null);
export function ThemeProvider({ children }: { children: React.ReactNode }) {
const [theme, setTheme] = useState<'light' | 'dark'>('light');
const toggleTheme = useCallback(() => {
setTheme(t => t === 'light' ? 'dark' : 'light');
}, []);
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
}
export function useTheme() {
const context = useContext(ThemeContext);
if (!context) throw new Error('useTheme must be used within ThemeProvider');
return context;
}
Controlled (recommended):
function ControlledInput() {
const [value, setValue] = useState('');
return (
<input
value={value}
onChange={e => setValue(e.target.value)}
/>
);
}
Uncontrolled:
function UncontrolledInput() {
const inputRef = useRef<HTMLInputElement>(null);
const handleSubmit = () => {
console.log(inputRef.current?.value);
};
return <input ref={inputRef} />;
}
Unnecessary re-renders:
Props drilling:
Stale closures in hooks:
setState(prev => prev + 1)Memory leaks:
npx claudepluginhub p/armanzeroeight-react-toolkit-plugins-react-toolkitProvides React patterns like compound components, render props, custom hooks, and HOCs for scalable frontend apps. Use for component design, state management, and best practices.
Implements React component patterns: compound components with context, render props, HOCs, custom hooks, provider with reducer, controlled/uncontrolled, prop getters, state reducer.
Provides expert guidance on React hooks, composition patterns, and performance optimization. Automatically activates when discussing hooks, state management, re-renders, or component composition.