Cursor can generate Redux actions and reducers quickly, but applying suggestions often creates merge conflicts with existing store slices. This tutorial shows how to use .cursorrules to enforce Redux Toolkit patterns, reference your existing store setup with @file, and safely accept Cursor suggestions without breaking your state management layer.
Using Cursor safely with Redux state management
Redux Toolkit enforces specific patterns for slices, actions, and reducers. When Cursor generates Redux code, it may conflict with your existing store structure by creating duplicate slice names, overwriting reducers, or using incompatible patterns. This guide shows you how to configure Cursor to generate Redux code that integrates cleanly with your existing setup.
Prerequisites
- Cursor installed with a React + Redux Toolkit project
- At least one Redux slice already created in your project
- Familiarity with Redux Toolkit's createSlice API
- Basic understanding of Cursor's Cmd+K and Cmd+L shortcuts
Step-by-step guide
Add Redux-specific rules to your project
Add Redux-specific rules to your project
Create a .cursor/rules file that enforces Redux Toolkit conventions. This prevents Cursor from generating legacy Redux patterns or conflicting with your existing store structure.
1---2description: Redux Toolkit conventions3globs: "src/store/**/*.ts,src/store/**/*.tsx"4alwaysApply: true5---67## Redux Rules8- ALWAYS use @reduxjs/toolkit createSlice, NOT manual action creators9- ALWAYS use createAsyncThunk for async operations, NEVER dispatch in useEffect10- Slice files go in src/store/slices/ with the pattern {feature}Slice.ts11- Export actions and reducer from each slice file12- Use the existing RootState and AppDispatch types from src/store/index.ts13- NEVER create a new store — only add slices to the existing one14- Use Immer (built into RTK) for state mutations in reducers15- Name async thunks as '{slice}/{action}' e.g. 'users/fetchAll'Expected result: Cursor will follow Redux Toolkit conventions whenever editing files in your store directory.
Add a new action to an existing slice with Cmd+K
Add a new action to an existing slice with Cmd+K
Open an existing slice file, place your cursor where you want the new reducer, select the reducers object, and press Cmd+K. Reference the slice file so Cursor sees the existing state shape and actions.
1// Open src/store/slices/userSlice.ts, select the reducers block,2// then press Cmd+K and type:3// "Add a 'setUserPreferences' reducer that accepts a4// payload of { theme: string; language: string } and5// merges it into the existing user state. Follow the6// same pattern as the other reducers in this file."78// Cursor generates:9setUserPreferences: (state, action: PayloadAction<{10 theme: string;11 language: string;12}>) => {13 state.preferences = {14 ...state.preferences,15 ...action.payload,16 };17},Pro tip: Select just the reducers block, not the entire file. This focuses Cursor on adding to existing code rather than rewriting it.
Expected result: Cursor inserts the new reducer into the existing reducers object without modifying other reducers.
Generate a new async thunk with proper typing
Generate a new async thunk with proper typing
Use Cursor Chat (Cmd+L) to generate an async thunk that integrates with your existing slice. Reference both the slice file and your store types so Cursor generates properly typed code.
1// Cursor Chat prompt (Cmd+L):2// @src/store/slices/userSlice.ts @src/store/index.ts3// Create a createAsyncThunk called 'users/updateProfile'4// that takes { name: string, email: string }, calls5// PUT /api/users/profile, and updates the user slice6// state. Add the extraReducers cases to the existing7// userSlice. Use our AppDispatch and RootState types.89// Expected output includes:10export const updateProfile = createAsyncThunk<11 User,12 { name: string; email: string },13 { state: RootState }14>('users/updateProfile', async (data, { rejectWithValue }) => {15 try {16 const response = await fetch('/api/users/profile', {17 method: 'PUT',18 headers: { 'Content-Type': 'application/json' },19 body: JSON.stringify(data),20 });21 if (!response.ok) throw new Error('Update failed');22 return response.json();23 } catch (err) {24 return rejectWithValue(err.message);25 }26});Expected result: Cursor generates a typed async thunk and the matching extraReducers cases for pending, fulfilled, and rejected states.
Register the new slice in the store
Register the new slice in the store
If you created a brand new slice, you need to add it to your root store. Use Cmd+K in the store index file to insert the new reducer. Reference the new slice file so Cursor generates the correct import.
1// Open src/store/index.ts, select the combineReducers2// or configureStore block, press Cmd+K:3// "Add the new orderSlice reducer from4// @src/store/slices/orderSlice.ts to the existing store.5// Do not modify any existing reducers."67// Cursor adds:8import orderReducer from './slices/orderSlice';910// And inserts into the reducer map:11const store = configureStore({12 reducer: {13 users: userReducer,14 products: productReducer,15 orders: orderReducer, // <-- new line only16 },17});Expected result: Cursor adds the import and reducer entry without touching existing store configuration.
Review changes before accepting
Review changes before accepting
Always review Cursor's Redux changes carefully in the diff view before accepting. Check that existing reducers are untouched, state shape is preserved, and action names do not conflict with existing ones. Use Cursor's checkpoint system to roll back if needed.
1// After Cursor generates changes, check:2// 1. No existing reducers were modified (look for red lines in diff)3// 2. Initial state shape is preserved4// 3. Action type strings are unique (no duplicates)5// 4. Imports do not conflict with existing imports6// 5. TypeScript types are correct78// If something went wrong, use the checkpoint:9// Click the restore icon in the Composer panel to revert10// all changes from the last operationPro tip: Enable Plan Mode (Shift+Tab) before complex Redux changes. Cursor will list what it plans to modify, letting you approve the scope before any code changes happen.
Expected result: Clean diff showing only additions with zero modifications to existing Redux logic.
Complete working example
1import { createSlice, createAsyncThunk, PayloadAction } from '@reduxjs/toolkit';2import type { RootState } from '../index';34interface Order {5 id: string;6 userId: string;7 items: Array<{ productId: string; quantity: number }>;8 status: 'pending' | 'confirmed' | 'shipped' | 'delivered';9 total: number;10 createdAt: string;11}1213interface OrderState {14 orders: Order[];15 currentOrder: Order | null;16 loading: boolean;17 error: string | null;18}1920const initialState: OrderState = {21 orders: [],22 currentOrder: null,23 loading: false,24 error: null,25};2627export const fetchOrders = createAsyncThunk<28 Order[],29 void,30 { state: RootState }31>('orders/fetchAll', async (_, { rejectWithValue }) => {32 try {33 const res = await fetch('/api/orders');34 if (!res.ok) throw new Error('Failed to fetch orders');35 return res.json();36 } catch (err) {37 return rejectWithValue((err as Error).message);38 }39});4041const orderSlice = createSlice({42 name: 'orders',43 initialState,44 reducers: {45 setCurrentOrder: (state, action: PayloadAction<Order>) => {46 state.currentOrder = action.payload;47 },48 clearError: (state) => {49 state.error = null;50 },51 },52 extraReducers: (builder) => {53 builder54 .addCase(fetchOrders.pending, (state) => {55 state.loading = true;56 state.error = null;57 })58 .addCase(fetchOrders.fulfilled, (state, action) => {59 state.loading = false;60 state.orders = action.payload;61 })62 .addCase(fetchOrders.rejected, (state, action) => {63 state.loading = false;64 state.error = action.payload as string;65 });66 },67});6869export const { setCurrentOrder, clearError } = orderSlice.actions;70export default orderSlice.reducer;Common mistakes
Why it's a problem: Letting Cursor rewrite the entire slice file instead of adding to it
How to avoid: Select only the specific section you want to modify (e.g., just the reducers block) before pressing Cmd+K. Tell Cursor to 'add to existing' not 'create a new slice.'
Why it's a problem: Cursor generating legacy Redux patterns (action constants, switch statements)
How to avoid: Add a .cursor/rules file mandating createSlice and createAsyncThunk from @reduxjs/toolkit. Explicitly forbid manual action constants.
Why it's a problem: Duplicate action type strings across slices
How to avoid: Add a rule requiring action type strings to follow the '{sliceName}/{actionName}' convention.
Best practices
- Always reference existing slice files with @file when generating new Redux code
- Select specific code sections for Cmd+K edits instead of entire files
- Use Plan Mode (Shift+Tab) to preview Cursor's planned changes before execution
- Add Redux Toolkit conventions to .cursor/rules to prevent legacy patterns
- Review diffs carefully for unintended modifications to existing reducers
- Use Cursor's checkpoint restore feature to undo bad Redux changes quickly
- Keep one slice per file and name files consistently as {feature}Slice.ts
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
Create a Redux Toolkit slice called orderSlice with: state for orders array, current order, loading, and error. Include a createAsyncThunk for fetching orders from /api/orders. Add reducers for setCurrentOrder and clearError. Use TypeScript with proper PayloadAction types.
In Cursor (Cmd+K, select the reducers block): @src/store/slices/orderSlice.ts Add a new reducer called 'updateOrderStatus' that takes { orderId: string, status: string } and updates the matching order in the orders array. Follow the same pattern as existing reducers. Do not modify other reducers.
Frequently asked questions
Is Cursor safe to use for modifying Redux store files?
Yes, with precautions. Always commit to Git before letting Cursor modify store files. Use Cmd+K on selected sections instead of full-file edits. Review diffs carefully before accepting.
Why does Cursor generate old-style Redux instead of Redux Toolkit?
Cursor's training data includes legacy Redux. Add a .cursor/rules file that explicitly requires createSlice and createAsyncThunk from @reduxjs/toolkit and forbids manual action constants.
Can Cursor handle RTK Query generation?
Yes. Reference your existing api.ts file with @file and ask Cursor to add new endpoints. Prompt: '@src/store/api.ts Add a new endpoint for PATCH /api/users/:id following the existing pattern.'
How do I prevent Cursor from creating a new store instance?
Add to your .cursorrules: 'NEVER create a new configureStore call. Only add slices to the existing store in src/store/index.ts.' Reference the store file in your prompt.
What if Cursor's Redux changes break my app?
Cursor creates automatic checkpoints before Agent mode changes. Click the restore icon in the Composer panel to revert. Alternatively, use git checkout to restore files from your pre-change commit.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation