
React State Management
Implement typed Redux Toolkit slices, store setup, and React hooks so agent-assisted UI work stays consistent in solo SaaS and dashboard builds.
Overview
react-state-management is an agent skill for the Build phase that teaches Redux Toolkit and TypeScript patterns for configuring stores, slices, async thunks, and typed React hooks.
Install
npx skills add https://github.com/wshobson/agents --skill react-state-managementWhat is this skill?
- Redux Toolkit configureStore setup with middleware and serializable-check tuning
- createSlice and createAsyncThunk patterns with typed rejectWithValue handling
- Typed useAppDispatch and useAppSelector hooks bound to RootState and AppDispatch
- Multi-slice store layout (e.g. user and cart) suitable for solo SaaS dashboards
- TypeScript-first slice state shapes with idle/loading/succeeded/failed status modeling
Adoption & trust: 9.7k installs on skills.sh; 36.5k GitHub stars; 3/3 security scanners passed (skills.sh audits).
What problem does it solve?
You are building a React app and global or feature state is turning into fragile context blobs, untyped Redux, or copy-paste store code the agent cannot extend consistently.
Who is it for?
Solo builders implementing or refactoring React client state in TypeScript apps with Redux Toolkit who want copy-paste-ready patterns for slices and typed hooks.
Skip if: Teams that have standardized on Zustand, Jotai, TanStack Query-only server state, or React Native stacks where this Redux-centric readme does not match your chosen stack.
When should I use this skill?
When implementing or refactoring React application state with Redux Toolkit and TypeScript during frontend build work.
What do I get? / Deliverables
After using the skill, you get a typed Redux Toolkit store layout with slices, async flows, and hook conventions your agent can replicate across new features.
- Typed store configuration and slice modules
- Typed dispatch and selector hooks wired to RootState
- Async slice flows with explicit loading and error status handling
Recommended Skills
Journey fit
React state architecture is decided and coded while building the product UI, before ship-time review and grow-phase analytics wiring. The skill centers on client-side React patterns—store configuration, slices, and typed selectors—not backend APIs or deploy pipelines.
How it compares
Use for procedural Redux Toolkit + TypeScript scaffolding instead of asking the agent for generic React state advice in unstructured chat.
Common Questions / FAQ
Who is react-state-management for?
It is for solo and indie builders shipping React web UIs who want agent-guided Redux Toolkit patterns with TypeScript rather than improvising store code each sprint.
When should I use react-state-management?
Use it in the Build phase on the frontend shelf when you add auth, cart, or dashboard state; when you migrate from useState sprawl; or when you need async thunk error handling before ship review.
Is react-state-management safe to install?
Treat it like any third-party skill: review the Security Audits panel on this Prism page and skim SKILL.md for shell or network permissions before enabling it in your agent.
SKILL.md
READMESKILL.md - React State Management
# react-state-management — detailed patterns and worked examples ## Patterns ### Pattern 1: Redux Toolkit with TypeScript ```typescript // store/index.ts import { configureStore } from "@reduxjs/toolkit"; import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux"; import userReducer from "./slices/userSlice"; import cartReducer from "./slices/cartSlice"; export const store = configureStore({ reducer: { user: userReducer, cart: cartReducer, }, middleware: (getDefaultMiddleware) => getDefaultMiddleware({ serializableCheck: { ignoredActions: ["persist/PERSIST"], }, }), }); export type RootState = ReturnType<typeof store.getState>; export type AppDispatch = typeof store.dispatch; // Typed hooks export const useAppDispatch: () => AppDispatch = useDispatch; export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector; ``` ```typescript // store/slices/userSlice.ts import { createSlice, createAsyncThunk, PayloadAction } from "@reduxjs/toolkit"; interface User { id: string; email: string; name: string; } interface UserState { current: User | null; status: "idle" | "loading" | "succeeded" | "failed"; error: string | null; } const initialState: UserState = { current: null, status: "idle", error: null, }; export const fetchUser = createAsyncThunk( "user/fetchUser", async (userId: string, { rejectWithValue }) => { try { const response = await fetch(`/api/users/${userId}`); if (!response.ok) throw new Error("Failed to fetch user"); return await response.json(); } catch (error) { return rejectWithValue((error as Error).message); } }, ); const userSlice = createSlice({ name: "user", initialState, reducers: { setUser: (state, action: PayloadAction<User>) => { state.current = action.payload; state.status = "succeeded"; }, clearUser: (state) => { state.current = null; state.status = "idle"; }, }, extraReducers: (builder) => { builder .addCase(fetchUser.pending, (state) => { state.status = "loading"; state.error = null; }) .addCase(fetchUser.fulfilled, (state, action) => { state.status = "succeeded"; state.current = action.payload; }) .addCase(fetchUser.rejected, (state, action) => { state.status = "failed"; state.error = action.payload as string; }); }, }); export const { setUser, clearUser } = userSlice.actions; export default userSlice.reducer; ``` ### Pattern 2: Zustand with Slices (Scalable) ```typescript // store/slices/createUserSlice.ts import { StateCreator } from "zustand"; export interface UserSlice { user: User | null; isAuthenticated: boolean; login: (credentials: Credentials) => Promise<void>; logout: () => void; } export const createUserSlice: StateCreator< UserSlice & CartSlice, // Combined store type [], [], UserSlice > = (set, get) => ({ user: null, isAuthenticated: false, login: async (credentials) => { const user = await authApi.login(credentials); set({ user, isAuthenticated: true }); }, logout: () => { set({ user: null, isAuthenticated: false }); // Can access other slices // get().clearCart() }, }); // store/index.ts import { create } from "zustand"; import { createUserSlice, UserSlice } from "./slices/createUserSlice"; import { createCartSlice, CartSlice } from "./slices/createCartSlice"; type StoreState = UserSlice & CartSlice; export const useStore = create<StoreState>()((...args) => ({ ...createUserSlice(...args), ...createCartSlice(...args), })); // Selective subscriptions (prevents unnecessary re-renders) export const useUser = () => useStore((state) => state.user); export const useCart = () => useStore((state) => state.cart); ``` ### Pattern 3: Jotai for Atomic State ```typescript // atoms/userAtoms.ts import { atom } from 'jotai' import { atomWithStorage } from 'jotai/util