Cursor defaults to Redux for almost any state management need because Redux appears heavily in its training data. For simple UI state, component-local state, or server cache, lighter solutions like useState, React Context, or SWR are better choices. This tutorial shows how to add state management guidance to .cursorrules so Cursor picks the right tool for each situation.
Stopping Cursor from overengineering state management
Not every piece of state needs Redux. Cursor suggests Redux for toggle booleans, form inputs, and even server-cached data because it is the most represented state library in its training data. This tutorial teaches Cursor when to use simpler alternatives.
Prerequisites
- Cursor installed with a React project
- Basic understanding of React state management options
- Familiarity with useState, Context, and Redux
- Cursor Chat (Cmd+L) access
Step-by-step guide
Add state management decision rules
Add state management decision rules
Create rules that map state types to appropriate solutions. This gives Cursor a decision tree instead of always defaulting to Redux.
1---2description: State management decision guide3globs: "src/**/*.tsx,src/**/*.ts"4alwaysApply: true5---67## State Management Decision Tree8| State Type | Solution | Example |9|-----------|---------|--------|10| Form input, toggle, local UI | useState | Modal open/close, input value |11| Shared UI state (2-3 components) | useState + prop drilling | Active tab shared by siblings |12| Theme, locale, auth | React Context | ThemeProvider, AuthProvider |13| Server data (read-heavy) | SWR or React Query | User list, product catalog |14| Complex client state (10+ actions) | Zustand | Shopping cart, editor state |15| Enterprise (middleware, devtools) | Redux Toolkit | Large team, complex workflows |1617## Rules18- NEVER use Redux for state shared by fewer than 5 components19- NEVER use Redux for server-cached data (use SWR/React Query)20- NEVER use Redux for form state (use react-hook-form or useState)21- Default to the SIMPLEST solution that works22- If unsure, ask the user which approach they preferExpected result: Cursor selects the simplest appropriate state solution based on the use case.
Prompt Cursor for simple state explicitly
Prompt Cursor for simple state explicitly
When you need local state, mention it explicitly in your prompt. Without guidance, Cursor may still reach for global state management.
1// Instead of: "Add state management for this modal"2// Say:34// Cursor Chat prompt (Cmd+L):5// Add an open/close toggle for this modal using useState.6// Do NOT use Redux, Context, or any state library.7// The state only needs to live in this component.89// Cursor generates:10const [isOpen, setIsOpen] = useState(false);11const openModal = () => setIsOpen(true);12const closeModal = () => setIsOpen(false);Pro tip: Be explicit about what NOT to use. Saying 'Do NOT use Redux' is more effective than just asking for useState.
Expected result: Simple useState-based state management without any global state library.
Redirect server state to SWR
Redirect server state to SWR
When Cursor tries to put API data in Redux, redirect it to SWR or React Query. Server state and client state are fundamentally different concerns.
1// If Cursor suggests Redux for fetching users:2// "Create a Redux slice for user data with fetchUsers thunk"34// Redirect with:5// Do NOT use Redux for this. User data is server state.6// Use SWR to fetch from /api/users with automatic caching7// and revalidation. Create a useUsers() hook in src/hooks/.89import useSWR from 'swr';10import { fetcher } from '@/lib/fetcher';1112export function useUsers() {13 const { data, error, isLoading } = useSWR('/api/users', fetcher);14 return { users: data ?? [], isLoading, isError: !!error };15}Expected result: Server data managed by SWR with automatic caching instead of a Redux slice.
Use Zustand for moderate complexity
Use Zustand for moderate complexity
When state is too complex for Context but does not need Redux's full machinery, direct Cursor to Zustand. It has a simpler API with built-in performance optimization.
1// Cursor Chat prompt (Cmd+L):2// Create a shopping cart store using Zustand (NOT Redux).3// State: items array, total, item count.4// Actions: addItem, removeItem, clearCart, updateQuantity.5// Use Zustand's create() function.67import { create } from 'zustand';89interface CartStore {10 items: CartItem[];11 addItem: (item: CartItem) => void;12 removeItem: (id: string) => void;13 clearCart: () => void;14 total: () => number;15}1617export const useCartStore = create<CartStore>((set, get) => ({18 items: [],19 addItem: (item) => set((s) => ({ items: [...s.items, item] })),20 removeItem: (id) => set((s) => ({ items: s.items.filter((i) => i.id !== id) })),21 clearCart: () => set({ items: [] }),22 total: () => get().items.reduce((sum, i) => sum + i.price * i.qty, 0),23}));Expected result: A Zustand store that provides the needed functionality without Redux boilerplate.
Evaluate when Redux IS appropriate
Evaluate when Redux IS appropriate
Redux is the right choice for large teams with complex middleware needs, time-travel debugging requirements, or when you need centralized devtools across many state slices.
1// Signs you actually need Redux:2// 1. More than 10 state slices with cross-slice dependencies3// 2. Complex middleware (logging, analytics, optimistic updates)4// 3. Large team needing enforced patterns and devtools5// 4. Need time-travel debugging for complex workflows6// 5. Existing Redux codebase that works well7//8// If none of these apply, use a simpler solution.Expected result: Clear criteria for when Redux is genuinely the right choice.
Complete working example
1---2description: State management decision guide3globs: "src/**/*.{ts,tsx}"4alwaysApply: true5---67## State Management Hierarchy (simplest first)89### 1. useState (default for local state)10- Form inputs, toggles, modals, local UI state11- State used by ONE component only12- No boilerplate, no imports beyond React1314### 2. useState + props (shared between parent/child)15- State shared by 2-3 closely related components16- Lift state to nearest common parent1718### 3. React Context (app-wide static state)19- Theme, locale, authentication status20- State that changes INFREQUENTLY21- Split state/dispatch contexts to avoid re-renders2223### 4. SWR or React Query (server state)24- Data fetched from APIs25- Automatic caching, revalidation, deduplication26- NEVER put server data in Redux2728### 5. Zustand (complex client state)29- Shopping cart, editor state, multi-step forms30- Simpler API than Redux, built-in performance31- Good for 3-8 state slices3233### 6. Redux Toolkit (enterprise complexity)34- 10+ state slices with middleware35- Large teams needing enforced patterns36- Time-travel debugging required3738## Anti-patterns to AVOID39- Redux for a single boolean toggle40- Redux for server-cached data (use SWR)41- Redux for form state (use react-hook-form)42- Context for frequently changing state (causes re-renders)43- Global state for component-local concernsCommon mistakes when stopping Cursor from overengineering solutions
Why it's a problem: Cursor creating a Redux slice for a modal toggle
How to avoid: Add the state management decision tree to .cursorrules. Explicitly state 'Use useState for this' in prompts for simple state.
Why it's a problem: Using Redux for API data that should be cached with SWR
How to avoid: Add 'NEVER put server data in Redux, use SWR' to your rules.
Why it's a problem: Adding Context for frequently updating state
How to avoid: Use Zustand for frequently updating shared state. It only re-renders components that subscribe to the specific value that changed.
Best practices
- Default to useState for local state; only escalate when needed
- Use SWR or React Query for server data, never Redux
- Add a state management decision tree to .cursorrules
- Be explicit in prompts about which state solution to use
- Use Zustand as the intermediate step between Context and Redux
- Evaluate Redux only for enterprise needs (10+ slices, middleware, large teams)
- State the constraint 'Do NOT use Redux' when simpler solutions suffice
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
Create a state management decision guide for a React TypeScript project. Define when to use: useState, props, Context, SWR, Zustand, and Redux Toolkit. Include concrete examples for each, anti-patterns to avoid, and a decision flowchart. Format as a .cursorrules file.
In Cursor Chat (Cmd+L): @.cursor/rules/state-management.mdc I need to manage a shopping cart. Based on our state management rules, which solution should I use? The cart has items, quantities, and a total. It is used by 4 components. Generate the implementation using the recommended approach.
Frequently asked questions
Is Redux ever the right choice?
Yes, for large applications with 10+ state slices, complex middleware needs, large teams needing enforced patterns, or when you need time-travel debugging. It is overkill for most small to medium projects.
Can I use Zustand and Redux in the same project?
Yes, but it is confusing. Pick one global state library per project. If you have existing Redux and want to add simpler state, Zustand can coexist but the team needs clear guidelines on when to use each.
How do I migrate from Redux to Zustand?
Convert one slice at a time. Create a Zustand store for the slice, update consumers to use the new store, remove the Redux slice. Ask Cursor to help: '@src/store/slices/cartSlice.ts Convert this Redux slice to a Zustand store.'
Why does Cursor suggest Redux so often?
Redux has been the dominant state management library since 2015. It has the most tutorials, examples, and documentation in Cursor's training data. The .cursorrules decision tree corrects this bias.
What about Jotai or Recoil?
Both are good alternatives. Jotai is atom-based (bottom-up), Recoil is similar but less maintained. Add your preferred library to the state management rules if you use them.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation