Cursor can generate Redux Toolkit code but often produces verbose, untyped slices with redundant boilerplate. By adding .cursor/rules/ that specify RTK conventions with createSlice, createAsyncThunk, and TypeScript generics, referencing your existing store configuration with @file, and providing typed patterns for Cursor to follow, you get clean, fully typed Redux slices with minimal code.
Generating Redux Toolkit code with Cursor
Redux Toolkit simplifies Redux with createSlice, createAsyncThunk, and built-in Immer. However, Cursor sometimes generates older Redux patterns with manual action creators, switch statements, and untyped state. This tutorial configures Cursor to generate modern RTK code with full TypeScript support.
Prerequisites
- Cursor installed with a React/TypeScript project
- @reduxjs/toolkit and react-redux installed
- A Redux store already configured
- Basic understanding of Redux Toolkit
Step-by-step guide
Create a Redux Toolkit rule for Cursor
Create a Redux Toolkit rule for Cursor
Add a project rule that specifies RTK patterns and bans legacy Redux patterns. Include your typed hooks and store configuration so Cursor uses them correctly.
1---2description: Redux Toolkit conventions3globs: "*slice.ts,*store.ts,*thunk.ts"4alwaysApply: true5---67# Redux Toolkit Rules8- ALWAYS use createSlice for reducer logic (never switch statements)9- ALWAYS use createAsyncThunk for async operations10- ALWAYS use PayloadAction<T> for typed action payloads11- ALWAYS use typed hooks: useAppSelector, useAppDispatch from @/store/hooks12- NEVER use useSelector or useDispatch directly (no type inference)13- NEVER use connect() HOC (use hooks instead)14- NEVER use manual action creators (createSlice generates them)15- Use EntityAdapter for normalized collections16- Use RTK Query for data fetching when possible1718## File pattern: src/features/{name}/{name}Slice.ts19## State type exported as {Name}State20## Selector functions exported as select{Property}Expected result: Cursor generates modern RTK code with createSlice, typed hooks, and no legacy patterns.
Generate a complete feature slice with async thunks
Generate a complete feature slice with async thunks
Reference your store hooks and an existing slice as a pattern. Ask for a complete feature slice including state, reducers, async thunks, and selectors.
1@redux-toolkit.mdc @src/store/hooks.ts @src/features/auth/authSlice.ts23Create a complete productsSlice following the same patterns as authSlice:41. State type: ProductsState with items, loading, error, selectedId, filters52. Async thunks: fetchProducts, fetchProductById, createProduct, updateProduct, deleteProduct63. Reducers: setFilters, setSelectedId, clearError74. Selectors: selectAllProducts, selectProductById, selectLoading, selectError85. Use PayloadAction<T> for all reducer payloads96. Handle loading/error states in extraReducers for all thunks1011Export everything: reducer (default), actions, thunks, selectors.Pro tip: Reference an existing slice with @file so Cursor matches your exact typing patterns. This is more reliable than describing the patterns in the prompt.
Expected result: Cursor generates a complete typed slice matching your existing patterns with createSlice, createAsyncThunk, and typed selectors.
Generate typed store hooks for Cursor to reference
Generate typed store hooks for Cursor to reference
Create typed useAppSelector and useAppDispatch hooks that Cursor should always import instead of the untyped react-redux hooks. This ensures type inference works across all generated components.
1import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';2import type { RootState, AppDispatch } from './store';34export const useAppDispatch: () => AppDispatch = useDispatch;5export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;Expected result: Typed hooks that Cursor imports in generated components for full type inference.
Generate a component that consumes the slice
Generate a component that consumes the slice
Ask Cursor to generate a React component that uses the typed hooks to interact with the Redux store. Reference both the slice and hooks so Cursor generates proper typed dispatch calls.
1@redux-toolkit.mdc @src/store/hooks.ts @src/features/products/productsSlice.ts23Create a ProductList component that:41. Uses useAppSelector to read products, loading, and error state52. Uses useAppDispatch to dispatch fetchProducts on mount63. Shows a loading spinner while fetching74. Shows an error message with retry button on failure85. Renders a list of products with name, price, and category96. Dispatches setSelectedId when a product is clicked1011Use the typed hooks from @/store/hooks, not raw useSelector/useDispatch.Expected result: Cursor generates a component using useAppSelector and useAppDispatch with full type inference from the slice.
Add RTK Query for data fetching
Add RTK Query for data fetching
For new features, RTK Query is often better than createAsyncThunk. Show Cursor how to generate an API slice with typed endpoints.
1@redux-toolkit.mdc @src/store/store.ts23Create an RTK Query API slice for products:41. Base URL: /api52. Endpoints: getProducts (query), getProductById (query), createProduct (mutation), updateProduct (mutation), deleteProduct (mutation)63. Tag types for cache invalidation: ['Product', 'Products']74. Generated hooks exported for use in components85. Proper TypeScript types for all request and response shapes910Add the API reducer and middleware to the store configuration.Expected result: Cursor generates a typed RTK Query API slice with auto-generated hooks and cache invalidation.
Complete working example
1import { createSlice, createAsyncThunk, PayloadAction } from '@reduxjs/toolkit';2import type { RootState } from '@/store/store';34interface Product {5 id: string;6 name: string;7 price: number;8 category: string;9}1011interface ProductFilters {12 category?: string;13 minPrice?: number;14 maxPrice?: number;15 search?: string;16}1718interface ProductsState {19 items: Product[];20 selectedId: string | null;21 filters: ProductFilters;22 loading: boolean;23 error: string | null;24}2526const initialState: ProductsState = {27 items: [],28 selectedId: null,29 filters: {},30 loading: false,31 error: null,32};3334export const fetchProducts = createAsyncThunk(35 'products/fetchAll',36 async (filters: ProductFilters, { rejectWithValue }) => {37 try {38 const params = new URLSearchParams(filters as Record<string, string>);39 const response = await fetch(`/api/products?${params}`);40 if (!response.ok) throw new Error('Failed to fetch products');41 return (await response.json()) as Product[];42 } catch (err) {43 return rejectWithValue((err as Error).message);44 }45 }46);4748const productsSlice = createSlice({49 name: 'products',50 initialState,51 reducers: {52 setFilters: (state, action: PayloadAction<ProductFilters>) => {53 state.filters = action.payload;54 },55 setSelectedId: (state, action: PayloadAction<string | null>) => {56 state.selectedId = action.payload;57 },58 clearError: (state) => {59 state.error = null;60 },61 },62 extraReducers: (builder) => {63 builder64 .addCase(fetchProducts.pending, (state) => {65 state.loading = true;66 state.error = null;67 })68 .addCase(fetchProducts.fulfilled, (state, action) => {69 state.loading = false;70 state.items = action.payload;71 })72 .addCase(fetchProducts.rejected, (state, action) => {73 state.loading = false;74 state.error = action.payload as string;75 });76 },77});7879export const { setFilters, setSelectedId, clearError } = productsSlice.actions;8081export const selectAllProducts = (state: RootState) => state.products.items;82export const selectLoading = (state: RootState) => state.products.loading;83export const selectError = (state: RootState) => state.products.error;8485export default productsSlice.reducer;Common mistakes when generating Redux Toolkit Code with Cursor
Why it's a problem: Cursor generates useSelector instead of useAppSelector
How to avoid: Create typed hooks in src/store/hooks.ts and add a rule: ALWAYS use useAppSelector and useAppDispatch from @/store/hooks.
Why it's a problem: Cursor generates switch-case reducers instead of createSlice
How to avoid: Add ALWAYS use createSlice and NEVER use switch statements for reducers to your rules.
Why it's a problem: Missing error handling in createAsyncThunk
How to avoid: Include error handling patterns in your rules with rejectWithValue for all async thunks.
Best practices
- Create typed hooks (useAppSelector, useAppDispatch) and reference them in rules
- Use createSlice for all reducer logic with PayloadAction<T> for typed payloads
- Use createAsyncThunk with rejectWithValue for proper error handling
- Reference an existing slice with @file when generating new features
- Export selectors alongside the slice for colocation
- Consider RTK Query for data fetching instead of manual async thunks
- Keep slice files under 150 lines by extracting complex thunks to separate files
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
Generate a Redux Toolkit slice for a shopping cart feature with TypeScript. Include state for items, totals, and loading. Use createSlice, createAsyncThunk with rejectWithValue, PayloadAction types, and typed selectors. Follow RTK best practices.
@redux-toolkit.mdc @src/store/hooks.ts @src/features/auth/authSlice.ts Create a notificationsSlice following the same patterns as authSlice. Include state for notifications array, unread count, and loading. Add async thunks for fetch, markRead, and dismiss. Export typed selectors.
Frequently asked questions
Should I use RTK Query or createAsyncThunk?
RTK Query for standard CRUD data fetching. createAsyncThunk for complex async logic with side effects like multi-step workflows, file uploads, or operations that update multiple slices.
How do I handle normalized state with Cursor?
Ask Cursor to use createEntityAdapter from RTK. Reference the adapter documentation in your rules and provide an example of the entity adapter pattern.
Can Cursor generate RTK Query code?
Yes. Reference your store configuration and ask for an API slice with createApi and fetchBaseQuery. Cursor generates typed endpoints with auto-generated hooks.
What about Zustand or Jotai instead of Redux?
If your app does not need Redux's middleware, devtools, or time-travel debugging, Zustand is simpler. Create separate rules for your chosen state management library.
How do I test Cursor-generated slices?
Ask Cursor to generate tests that dispatch actions and verify state changes. RTK slices are pure functions, making them easy to test without mocking.
Can RapidDev help with state management architecture?
Yes. RapidDev evaluates state management needs and implements the right solution, whether Redux Toolkit, Zustand, or React Context, with Cursor rules for ongoing consistency.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation