From software-development
React component patterns, hooks, and state management best practices for TypeScript. Loaded by frontend-software-developer-agent when building UI components, managing state, or structuring React applications.
npx claudepluginhub bartekck/bartek-marketplace --plugin software-developmentThis skill uses the workspace's default tool permissions.
Share implicit state between parent and children via context.
Provides modern React patterns for component design, hooks, state management, composition, performance, error handling, and TypeScript best practices.
Provides modern React patterns for component design, hooks, composition, state management, performance, error handling, TypeScript, and React 19 features. Useful for production-ready apps.
Provides React patterns for building components with props and composition, state management via Hooks/Zustand/Context, custom hooks, data fetching, performance optimization, and compound components like Tabs.
Share bugs, ideas, or general feedback.
Share implicit state between parent and children via context.
interface TabsContextType { active: string; setActive: (id: string) => void }
const TabsContext = createContext<TabsContextType | null>(null);
function Tabs({ children, defaultTab }: { children: ReactNode; defaultTab: string }) {
const [active, setActive] = useState(defaultTab);
return <TabsContext.Provider value={{ active, setActive }}>{children}</TabsContext.Provider>;
}
function TabPanel({ id, children }: { id: string; children: ReactNode }) {
const ctx = useContext(TabsContext);
if (!ctx) throw new Error("TabPanel must be inside Tabs");
return ctx.active === id ? <>{children}</> : null;
}
// Usage: <Tabs defaultTab="a"><TabPanel id="a">Content</TabPanel></Tabs>
Delegate rendering to the consumer when you need flexible output.
interface ListProps<T> { items: T[]; renderItem: (item: T, index: number) => ReactNode }
function List<T>({ items, renderItem }: ListProps<T>) {
return <ul>{items.map((item, i) => <li key={i}>{renderItem(item, i)}</li>)}</ul>;
}
function withAuth<P extends object>(Component: ComponentType<P>) {
return function AuthWrapper(props: P) {
const { user } = useAuth();
if (!user) return <Navigate to="/login" />;
return <Component {...props} />;
};
}
function useApi<T>(url: string): { data: T | null; loading: boolean; error: string | null } {
const [state, setState] = useState<{ data: T | null; loading: boolean; error: string | null }>({
data: null, loading: true, error: null,
});
useEffect(() => {
const controller = new AbortController();
fetch(url, { signal: controller.signal })
.then((res) => res.json() as Promise<T>)
.then((data) => setState({ data, loading: false, error: null }))
.catch((err) => { if (!controller.signal.aborted) setState({ data: null, loading: false, error: err.message }); });
return () => controller.abort();
}, [url]);
return state;
}
function useDebounce<T>(value: T, delay: number): T {
const [debounced, setDebounced] = useState(value);
useEffect(() => { const id = setTimeout(() => setDebounced(value), delay); return () => clearTimeout(id); }, [value, delay]);
return debounced;
}
| Approach | Use when |
|---|---|
useState | Simple local state, toggles, form fields |
useReducer | Complex state with multiple sub-values or transitions |
| Context | Shared state across a subtree (theme, auth, locale) |
| React Query / SWR | Server state — caching, revalidation, pagination |
| Zustand / Jotai | Global client state that many components read/write |
Rule: Server state belongs in React Query, not in global stores.
Controlled — React state is the source of truth. Use when you need validation, formatting, or conditional logic on every change.
function ControlledInput({ value, onChange }: { value: string; onChange: (v: string) => void }) {
return <input value={value} onChange={(e) => onChange(e.target.value)} />;
}
Uncontrolled — DOM is the source of truth. Use for simple forms or when integrating non-React libraries.
function UncontrolledForm({ onSubmit }: { onSubmit: (data: FormData) => void }) {
const ref = useRef<HTMLFormElement>(null);
return <form ref={ref} onSubmit={(e) => { e.preventDefault(); onSubmit(new FormData(ref.current!)); }}>
<input name="email" defaultValue="" />
<button type="submit">Submit</button>
</form>;
}
function useUsers() {
return useQuery<User[]>({ queryKey: ["users"], queryFn: () => fetch("/api/users").then((r) => r.json()) });
}
function useCreateUser() {
const qc = useQueryClient();
return useMutation({
mutationFn: (data: CreateUserDto) => fetch("/api/users", { method: "POST", body: JSON.stringify(data) }),
onSuccess: () => qc.invalidateQueries({ queryKey: ["users"] }),
});
}
useMemo, callback references with useCallback — but only when profiling shows a need.useEffect for subscriptions, timers, and abort controllers.