frontend
Skill190 starsChanged 6 months agoMITView on GitHub
Use when making architecture-level React decisions — component decomposition, choosing where state should live, selecting a state manager (Context vs Redux vs Zustand) or a data-fetching library (TanStack Query vs SWR), or planning a rendering-performance budget. For advanced hook patterns, use react.
Files
Frontend Patterns
Conventions and best practices for building maintainable, performant React applications.
When to Activate
- Designing component structure or deciding where state should live
- Choosing between local state, Context, Zustand, or Redux Toolkit
- Implementing data fetching, caching, or server state synchronization
- Building forms with validation and submission handling
- Optimizing rendering performance (re-renders, bundle size, lazy loading)
- Setting up routing with protected routes or nested layouts
- Writing tests for React components and hooks
Component Design
Single Responsibility
// BAD: component does too many things
function UserDashboard({ userId }: { userId: string }) {
const [user, setUser] = useState(null);
const [orders, setOrders] = useState([]);
useEffect(() => { /* fetch user + orders */ }, []);
return <div>{/* render user, orders, sidebar, notifications */}</div>;
}
// GOOD: split by concern
function UserDashboard({ userId }: { userId: string }) {
return (
<DashboardLayout>
<UserProfile userId={userId} />
<OrderList userId={userId} />
<NotificationPanel userId={userId} />
</DashboardLayout>
);
}Composition over Props Drilling
// BAD: prop drilling through 3+ levels
<Page user={user} onLogout={onLogout} theme={theme} />
<Header user={user} onLogout={onLogout} theme={theme} />
<Nav user={user} onLogout={onLogout} theme={theme} />
// GOOD: compound component or slot pattern
<Page>
<Header>
<Nav />
<UserMenu />
</Header>
<main>{children}</main>
</Page>Container / Presentational Split
// Container: owns data fetching and state
function OrderListContainer({ userId }: { userId: string }) {
const { data, isLoading, error } = useOrders(userId);
if (isLoading) return <OrderListSkeleton />;
if (error) return <ErrorBoundary error={error} />;
return <OrderList orders={data} />;
}
// Presentational: pure rendering, easily testable
function OrderList({ orders }: { orders: Order[] }) {
return (
<ul>
{orders.map(o => <OrderItem key={o.id} order={o} />)}
</ul>
);
}State Management
Decision Matrix
| State Type | Tool | Examples |
| UI / ephemeral | useState / useReducer | modal open, input value, toggle |
| Shared UI state | Zustand or Context | theme, sidebar collapsed, selected tab |
| Server state | TanStack Query | API responses, lists, detail pages |
| Complex client state | Redux Toolkit | shopping cart, multi-step wizard, offline queue |
| URL state | Search params | filters, pagination, selected item |
| Form state | React Hook Form | field values, validation, submission |
Zustand (preferred for shared UI state)
import { create } from "zustand";
interface SidebarStore {
isOpen: boolean;
toggle: () => void;
open: () => void;
close: () => void;
}
const useSidebarStore = create<SidebarStore>((set) => ({
isOpen: false,
toggle: () => set((s) => ({ isOpen: !s.isOpen })),
open: () => set({ isOpen: true }),
close: () => set({ isOpen: false }),
}));
// With persistence
import { persist } from "zustand/middleware";
const useThemeStore = create(persist(
(set) => ({ theme: "light", setTheme: (t) => set({ theme: t }) }),
{ name: "theme-storage" },
));Redux Toolkit (complex client state)
import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
const fetchOrders = createAsyncThunk("orders/fetch", async (userId: string) => {
const res = await fetch(`/api/users/${userId}/orders`);
return res.json();
});
const ordersSlice = createSlice({
name: "orders",
initialState: { items: [] as Order[], status: "idle" as LoadingStatus },
reducers: {
removeOrder: (state, action) => {
state.items = state.items.filter(o => o.id !== action.payload);
},
},
extraReducers: (builder) => {
builder
.addCase(fetchOrders.pending, (state) => { state.status = "loading"; })
.addCase(fetchOrders.fulfilled, (state, action) => {
state.status = "succeeded";
state.items = action.payload;
})
.addCase(fetchOrders.rejected, (state) => { state.status = "failed"; });
},
});Data Fetching
TanStack Query (server state)
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
// Fetching
function useOrders(userId: string) {
return useQuery({
queryKey: ["orders", userId],
queryFn: () => fetchOrders(userId),
staleTime: 5 * 60 * 1000, // 5 min — don't refetch if fresh
gcTime: 10 * 60 * 1000, // 10 min — keep in cache after unmount
});
}
// Mutation with optimistic update
function useCancelOrder() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (orderId: string) => cancelOrder(orderId),
onMutate: async (orderId) => {
await queryClient.cancelQueries({ queryKey: ["orders"] });
const prev = queryClient.getQueryData(["orders"]);
queryClient.setQueryData(["orders"], (old: Order[]) =>
old.filter(o => o.id !== orderId),
);
return { prev };
},
onError: (_, __, ctx) => {
queryClient.setQueryData(["orders"], ctx?.prev);
},
onSettled: () => queryClient.invalidateQueries({ queryKey: ["orders"] }),
});
}Query Key Conventions
// Hierarchical keys enable targeted invalidation
["users"] // all users
["users", userId] // one user
["users", userId, "orders"] // user's orders
["users", userId, "orders", { status: "active" }] // filtered
queryClient.invalidateQueries({ queryKey: ["users", userId] });
// ^ invalidates user + all sub-keys (orders, profile, etc.)Forms
React Hook Form + Zod
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
const schema = z.object({
email: z.string().email("Invalid email"),
password: z.string().min(8, "At least 8 characters"),
role: z.enum(["admin", "user"]),
});
type FormValues = z.infer<typeof schema>;
function CreateUserForm() {
const { register, handleSubmit, formState: { errors, isSubmitting } } =
useForm<FormValues>({ resolver: zodResolver(schema) });
const onSubmit = async (data: FormValues) => {
await createUser(data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register("email")} />
{errors.email && <p>{errors.email.message}</p>}
<input type="password" {...register("password")} />
{errors.password && <p>{errors.password.message}</p>}
<button type="submit" disabled={isSubmitting}>
{isSubmitting ? "Creating…" : "Create User"}
</button>
</form>
);
}Routing (React Router v6)
Nested Layouts with Protected Routes
import { createBrowserRouter, RouterProvider, Outlet, Navigate } from "react-router-dom";
function RequireAuth() {
const { user } = useAuth();
return user ? <Outlet /> : <Navigate to="/login" replace />;
}
const router = createBrowserRouter([
{ path: "/login", element: <LoginPage /> },
{
element: <RequireAuth />,
children: [
{
element: <AppShell />, // persistent layout (nav, sidebar)
children: [
{ path: "/", element: <Dashboard /> },
{ path: "/orders", element: <OrderList /> },
{ path: "/orders/:id", element: <OrderDetail />,
loader: ({ params }) => fetchOrder(params.id!) },
],
},
],
},
]);URL State for Filters
import { useSearchParams } from "react-router-dom";
function OrderFilters() {
const [params, setParams] = useSearchParams();
const status = params.get("status") ?? "all";
return (
<select value={status} onChange={e =>
setParams(p => { p.set("status", e.target.value); return p; })
}>
<option value="all">All</option>
<option value="active">Active</option>
</select>
);
}Performance
Memoization — when it helps
// useMemo: expensive derivation, stable reference for children
const sortedOrders = useMemo(
() => [...orders].sort((a, b) => b.total - a.total),
[orders],
);
// useCallback: stable reference passed to memo'd child or as dep
const handleCancel = useCallback((id: string) => cancelOrder(id), [cancelOrder]);
// memo: skip re-render when props unchanged
const OrderItem = memo(function OrderItem({ order }: { order: Order }) {
return <li>{order.id}</li>;
});
// BAD: wrapping everything — memo adds overhead, only helps when re-renders are expensiveCode Splitting
import { lazy, Suspense } from "react";
// Route-level splitting (most impactful)
const Dashboard = lazy(() => import("./pages/Dashboard"));
const Reports = lazy(() => import("./pages/Reports"));
function App() {
return (
<Suspense fallback={<PageSkeleton />}>
<Routes>
<Route path="/" element={<Dashboard />} />
<Route path="/reports" element={<Reports />} />
</Routes>
</Suspense>
);
}Virtualization for Long Lists
import { useVirtualizer } from "@tanstack/react-virtual";
function VirtualOrderList({ orders }: { orders: Order[] }) {
const parentRef = useRef<HTMLDivElement>(null);
const virtualizer = useVirtualizer({
count: orders.length,
getScrollElement: () => parentRef.current,
estimateSize: () => 64,
});
return (
<div ref={parentRef} style={{ height: "600px", overflow: "auto" }}>
<div style={{ height: virtualizer.getTotalSize() }}>
{virtualizer.getVirtualItems().map(item => (
<div key={item.key} style={{ transform: `translateY(${item.start}px)` }}>
<OrderItem order={orders[item.index]} />
</div>
))}
</div>
</div>
);
}Testing
Component Tests (React Testing Library)
import { render, screen, fireEvent, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
test("submits form with valid data", async () => {
const user = userEvent.setup();
const onSubmit = vi.fn();
render(<CreateUserForm onSubmit={onSubmit} />);
await user.type(screen.getByLabelText("Email"), "alice@example.com");
await user.type(screen.getByLabelText("Password"), "secretpass");
await user.click(screen.getByRole("button", { name: "Create User" }));
await waitFor(() => expect(onSubmit).toHaveBeenCalledWith({
email: "alice@example.com",
password: "secretpass",
}));
});
test("shows validation errors", async () => {
const user = userEvent.setup();
render(<CreateUserForm onSubmit={vi.fn()} />);
await user.click(screen.getByRole("button", { name: "Create User" }));
expect(await screen.findByText("Invalid email")).toBeInTheDocument();
});Testing Hooks
import { renderHook, act } from "@testing-library/react";
test("useCounter increments", () => {
const { result } = renderHook(() => useCounter(0));
act(() => result.current.increment());
expect(result.current.count).toBe(1);
});See also:
unit-testing,accessibility
Red Flags
- Data fetching inside
useEffectwithout a library — manual fetch-in-effect produces race conditions, missing loading/error states, and no deduplication; use TanStack Query or SWR - Global state for server data — storing server-fetched data in Redux/Zustand duplicates cache logic already solved by a data fetching library; keep server state in the fetching layer
key={index}in lists — using array index as key breaks React reconciliation when items reorder or are inserted; use a stable, unique ID from the data- Uncontrolled forms for complex validation — uncontrolled inputs with
refcan't drive real-time validation or conditional fields; use React Hook Form with Zod schema validation useEffectto sync derived state — computing derived values in an effect causes an extra render cycle; compute them inline during render or memoize withuseMemo- Prop drilling more than 2 levels — passing props through 3+ components is a sign the tree needs restructuring or a context/selector; don't reach for global state before considering composition
- No
Suspenseboundary around lazy-loaded routes — code-split routes without a fallback show a blank screen during load; wrap every lazy route in<Suspense fallback={<Skeleton />}>
Checklist
- Components have a single clear responsibility — split if rendering + fetching + formatting
- Server state managed with TanStack Query, not
useEffect+useState - Forms use React Hook Form with Zod schema validation
- Shared client state uses Zustand (simple) or Redux Toolkit (complex)
- URL state (filters, pagination, selected ID) stored in search params
- Route-level code splitting applied to all page components
- Lists over ~100 items virtualized
useMemo/useCallback/memoapplied only where profiling confirms re-render cost- Components tested via React Testing Library (user interactions, not implementation)
- Accessible: semantic HTML, labels on inputs, keyboard navigation verified
- No prop drilling beyond 2 levels — use composition, Context, or Zustand
Other versions48 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 48
- langwatch/langwatch★ 5ksame name
- atopile/atopile★ 4ksame name
- leookun/cursor-byok★ 3.2ksame name
- theopenco/llmgateway★ 1.7ksame name
- notque/vexjoy-agent★ 443same name
- notque/claude-code-toolkit★ 442same name
- AlexPEClub/ai-coding-starter-kit★ 382same name
- OyadotAI/oya-browser★ 351same name
- speakeasy-api/gram★ 275same name
- ThePrimeagen/skills★ 234same name
38 more not listed
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
Used alongsideProjects with it also use programming, backend and 9 more.
Other files people use alongside this one.
Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
DevNewbie1826Skillprogramming
MUST USE for work on Python, Rust, TypeScript, or Go source files and project manifests. Triggers: write or edit code; new project; Go HTTP service (gin lane); Bubble Tea TUI; CJK IME; Connect-Go RPC; sqlc or pgx; branded IDs; exhaustive matching; unsafe Rust; Miri; oversized file; refactoring; logging, log levels, structured logging, or observability; arena, allocator, bumpalo, const fn, const generics, compile-time programming, zero-allocation APIs, bitfields, repr, scope guards, errdefer-style cleanup, Zig-like patterns, zero-copy parsing, packed data, or FFI; Rust public API design or macros.
Also in 3 projects that use this
wewpellex21Skillbackend
Servers, routes, APIs, and backend architecture. Activated when Claude creates server-side code, API routes, or Express/Fastify/Next.js API handlers.
Also in 7 projects that use this
launch52-aiSkilldata
Generate data layer code (Freezed DTOs, repository implementations, data sources) from feature specifications or API references. Creates the bridge between domain entities and external data sources. Use after /domain to implement the data layer.
Also in 4 projects that use this
AnthropicSkillarchitecture
Create or evaluate an architecture decision record (ADR). Use when choosing between technologies (e.g., Kafka vs SQS), documenting a design decision with trade-offs and consequences, reviewing a system design proposal, or designing a new component from requirements and constraints.
Also in 7 projects that use this
BlockSkilldebugging
Debugging guidelines and best practices
Also in 5 projects that use this
MicrosoftSkillhelp
Entry point for the MedImageInsight plugin, covering the getting-started walkthrough, how the skills work, what MI2 is, and troubleshooting.
Also in 4 projects that use this
In this repository65 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- accessibilitySkill
- agentexSkill
- ai-engineerSkill
- angularSkill
- api-designSkill
- authSkill
- awsSkill
- azure-service-busSkill
- azureSkill
- cachingSkill
- ci-cdSkill
- claude-apiSkill
- claude-codeSkill
- coding-standardsSkill
- complex-doc-ragSkill
- containerizationSkill
- database-designSkill
- data-pipelinesSkill
- deployment-strategiesSkill
- development-workflowSkill
- dockerSkill
- error-handlingSkill
- event-drivenSkill
- fastapiSkill
- feature-flagsSkill
- general-temporalSkill
- github-issuesSkill
- goSkill
- graphqlSkill
- incident-responseSkill
- infrastructure-as-codeSkill
- integration-testingSkill
- langgraphSkill
- memory-mapSkill
- microservicesSkill
- mongodbSkill
- nextjsSkill
- observabilitySkill
- openai-agentsSkill
- performanceSkill
- performance-testingSkill
- postgresqlSkill
- promptbaseSkill
- pydanticSkill
- pythonSkill
- reactSkill
- redisSkill
- requirements-planningSkill
- securitySkill
- smoke-testingSkill
- solution-testingSkill
- spellbook-setupSkill
- sqlalchemySkill
- system-designSkill
- tailwindSkill
- technical-documentationSkill
- temporalSkill
- test-strategySkill
- typescriptSkill
- The first 60 shown.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
Reviews can't be read right now.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

