| name | frontend-patterns |
| type | reference |
| description | Framework-agnostic React/Vue patterns — component composition, hooks, TanStack Query, memoization, error boundaries. Use for generic React/Vue work (Vite, CRA, Storybook). For Next.js App Router / Server Components specifically, use `senior-frontend` instead. |
| paths | ["**/*.tsx","**/*.jsx","**/*.vue","**/components/**"] |
| effort | 3 |
| allowed-tools | Read, Glob, Grep, Write, Edit, Bash |
| user-invocable | true |
| when_to_use | When building React/Vue components, hooks, state management, or client-side performance — NOT for Next.js App Router (see `senior-frontend`) |
Frontend Patterns
Critical rules (non-obvious)
- Stale closure in
useEffect: always list all dependencies; use useRef for values that shouldn't trigger re-run
useEffect with async: never make the callback async directly — create inner async fn and call it
- Object/array as dependency: memoize with
useMemo/useCallback or use primitive values; otherwise infinite loop
- Key prop on lists: use stable IDs, never
index when list can reorder or items get deleted
React.memo is not free: only wrap components with expensive renders and stable prop references
Component composition patterns
const TabsContext = createContext<{ active: string; setActive: (v: string) => void } | null>(null);
function Tabs({ children, defaultValue }: { children: React.ReactNode; defaultValue: string }) {
const [active, setActive] = useState(defaultValue);
return <TabsContext.Provider value={{ active, setActive }}>{children}</TabsContext.Provider>;
}
Tabs.Trigger = function TabsTrigger() {
ctx = ()!;
;
};
. = () {
{ active } = ()!;
active === value ? : ;
};