frontend/managing-state/SKILL.md
Manage frontend state using Zustand for client state, TanStack Query for server state, and React Context for global static values.
npx skillsauth add 7a336e6e/skills managing-stateInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
4 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Choose the right state management tool for each category of state and implement typed, performant stores that minimize unnecessary re-renders.
| Question | Answer |
|----------------------------------------------|----------------------------|
| Does the data come from an API? | TanStack Query |
| Is it shared client state across pages? | Zustand store |
| Is it local to one component? | useState / useReducer |
| Is it global and rarely changes (theme, i18n)? | React Context |
Create a typed store with selectors to prevent full-store re-renders:
// stores/authStore.ts
import { create } from "zustand";
import { devtools, persist } from "zustand/middleware";
interface AuthState {
token: string | null;
user: { id: string; name: string } | null;
setAuth: (token: string, user: AuthState["user"]) => void;
logout: () => void;
}
export const useAuthStore = create<AuthState>()(
devtools(
persist(
(set) => ({
token: null,
user: null,
setAuth: (token, user) => set({ token, user }),
logout: () => set({ token: null, user: null }),
}),
{ name: "auth-storage" },
),
),
);
// Selectors -- consume only the slice you need
export const useToken = () => useAuthStore((s) => s.token);
export const useCurrentUser = () => useAuthStore((s) => s.user);
Use queries for reads and mutations for writes. Invalidate related queries after mutations:
// hooks/useUsers.ts
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import { getUsers, createUser } from "@/services/userService";
import type { User, CreateUserPayload } from "@/types/user";
export function useUsers() {
return useQuery<User[]>({
queryKey: ["users"],
queryFn: getUsers,
staleTime: 5 * 60 * 1000,
});
}
export function useCreateUser() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (payload: CreateUserPayload) => createUser(payload),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["users"] });
},
});
}
For mutations where instant feedback matters:
export function useUpdateUser() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (user: User) => updateUser(user),
onMutate: async (updatedUser) => {
await queryClient.cancelQueries({ queryKey: ["users"] });
const previous = queryClient.getQueryData<User[]>(["users"]);
queryClient.setQueryData<User[]>(["users"], (old) =>
old?.map((u) => (u.id === updatedUser.id ? updatedUser : u)),
);
return { previous };
},
onError: (_err, _user, context) => {
queryClient.setQueryData(["users"], context?.previous);
},
onSettled: () => {
queryClient.invalidateQueries({ queryKey: ["users"] });
},
});
}
Use only for values that change infrequently and are needed throughout the tree:
// contexts/ThemeContext.tsx
import { createContext, useContext, useState, type ReactNode } from "react";
type Theme = "light" | "dark";
interface ThemeContextValue {
theme: Theme;
toggle: () => void;
}
const ThemeContext = createContext<ThemeContextValue | null>(null);
export function ThemeProvider({ children }: { children: ReactNode }) {
const [theme, setTheme] = useState<Theme>("light");
const toggle = () => setTheme((t) => (t === "light" ? "dark" : "light"));
return (
<ThemeContext.Provider value={{ theme, toggle }}>
{children}
</ThemeContext.Provider>
);
}
export function useTheme() {
const ctx = useContext(ThemeContext);
if (!ctx) throw new Error("useTheme must be used within <ThemeProvider>");
return ctx;
}
devtools middleware in Zustand during developmentsetState directly from inside render (causes infinite loops)src/stores/ with exported selectorssrc/hooks/ wrapping service functionssrc/contexts/ only for theme, locale, or similardevelopment
Implement features using the Red-Green-Refactor cycle to ensure testability and correctness from the start.
data-ai
Manage the `tasks.md` ledger with strict locking and collision avoidance protocols to allow multiple agents to work in parallel safely.
development
The git-workflow skill defines branching conventions, commit message formats, and pull request standards that all agents must follow for consistent version control.
development
The environment-config skill standardizes how agents manage environment variables, secrets, and application configuration across local development and deployed environments.