build-modern-redux-apps/modern-redux
Use this when setting up a new Redux Toolkit app or modernizing an existing React + Redux codebase. Covers configureStore, Provider wiring, typed hooks, hooks-first React-Redux usage, feature folders, and the correct store lifetime for SPA and SSR-heavy React environments.
- 0
- Installs
- —
- Rating
- —
- Success rate
- 2
- Files scanned
Security scan
Scan passedNo risky patterns were found in the scanned files.
Content sha256 64aab49f533b79f2… — run codexguild_scan_skills after installing to verify your local copy.
Static analysis is a first line of defense, not a guarantee. Read the source
SKILL.md
Modern Redux
Setup
// file: src/features/counter/counterSlice.ts
import { createSlice } from '@reduxjs/toolkit'
export const counterSlice = createSlice({
name: 'counter',
initialState: { value: 0 },
reducers: {
increment(state) {
state.value += 1
},
},
})
export const { increment } = counterSlice.actions
// file: src/app/store.ts
import { configureStore } from '@reduxjs/toolkit'
import { counterSlice } from '../features/counter/counterSlice'
export const store = configureStore({
reducer: {
counter: counterSlice.reducer,
},
})
export type RootState = ReturnType<typeof store.getState>
export type AppDispatch = typeof store.dispatch
// file: src/app/hooks.ts
import { useDispatch, useSelector } from 'react-redux'
import type { AppDispatch, RootState } from './store'
export const useAppDispatch = useDispatch.withTypes<AppDispatch>()
export const useAppSelector = useSelector.withTypes<RootState>()
// file: src/features/counter/Counter.tsx
import { increment } from './counterSlice'
import { useAppDispatch, useAppSelector } from '../../app/hooks'
export function Counter() {
const value = useAppSelector((state) => state.counter.value)
const dispatch = useAppDispatch()
return <button onClick={() => dispatch(increment())}>{value}</button>
}
// file: src/main.tsx
import React from 'react'
import ReactDOM from 'react-dom/client'
import { Provider } from 'react-redux'
import { store } from './app/store'
import { Counter } from './features/counter/Counter'
ReactDOM.createRoot(document.getElementById('root')!).render(
<Provider store={store}>
<Counter />
</Provider>,
)
Core Patterns
Keep React components on hooks, not wrappers
import { postAdded, selectPosts } from './postsSlice'
import { useAppDispatch, useAppSelector } from '../../app/hooks'
export function PostsList() {
const posts = useAppSelector(selectPosts)
const dispatch = useAppDispatch()
return (
<button
onClick={() => dispatch(postAdded({ id: 'p2', title: 'Write docs' }))}
>
{posts.length}
</button>
)
}
Hooks are the default React-Redux integration for new code.
Create the store inside the provider for SSR-heavy React apps
// file: src/lib/store.ts
import { configureStore } from '@reduxjs/toolkit'
import { counterSlice } from '../features/counter/counterSlice'
export const makeStore = () =>
configureStore({
reducer: {
counter: counterSlice.reducer,
},
})
export type AppStore = ReturnType<typeof makeStore>
export type RootState = ReturnType<AppStore['getState']>
export type AppDispatch = AppStore['dispatch']
// file: src/app/StoreProvider.tsx
;('use client')
import { useState, type ReactNode } from 'react'
import { Provider } from 'react-redux'
import { makeStore } from '../lib/store'
export function StoreProvider({ children }: { children: ReactNode }) {
const [store] = useState(makeStore)
return <Provider store={store}>{children}</Provider>
}
In SSR-heavy React frameworks, create a store per request and keep that instance stable across renders.
Keep app wiring in app/ and feature logic in feature folders
src/
app/
store.ts
hooks.ts
features/
posts/
postsSlice.ts
PostsList.tsx
users/
usersSlice.ts
UsersList.tsx
This keeps store wiring centralized and feature logic colocated.
Common Mistakes
HIGH Importing the store in React components
Wrong:
import { store } from '../../app/store'
export function PostsList() {
const posts = store.getState().posts
return <div>{posts.length}</div>
}
Correct:
import { useAppSelector } from '../../app/hooks'
export function PostsList() {
const posts = useAppSelector((state) => state.posts)
return <div>{posts.length}</div>
}
React components should read through context and hooks; direct store imports are a separate escape hatch for non-React integrations, not the default UI pattern.
Source: reduxjs/redux:docs/style-guide/style-guide.md
HIGH Defaulting to connect() in new React code
Wrong:
import { connect } from 'react-redux'
const increment = () => ({ type: 'counter/increment' as const })
const mapState = (state: { counter: { value: number } }) => ({
value: state.counter.value,
})
const mapDispatch = { increment }
function Counter({
value,
increment,
}: ReturnType<typeof mapState> & typeof mapDispatch) {
return <button onClick={() => increment()}>{value}</button>
}
export default connect(mapState, mapDispatch)(Counter)
Correct:
import { increment } from './counterSlice'
import { useAppDispatch, useAppSelector } from '../../app/hooks'
export function Counter() {
const value = useAppSelector((state) => state.counter.value)
const dispatch = useAppDispatch()
return <button onClick={() => dispatch(increment())}>{value}</button>
}
Hooks are the modern default, simpler to type, and the maintainers explicitly want agents to steer new code away from connect.
Source: reduxjs/redux-toolkit:docs/usage/migrating-to-modern-redux.mdx
HIGH Recreating the store during render in SSR-heavy apps
Wrong:
'use client'
import { Provider } from 'react-redux'
import { makeStore } from '../lib/store'
export function StoreProvider({
children,
}: {
children: import('react').ReactNode
}) {
const store = makeStore()
return <Provider store={store}>{children}</Provider>
}
Correct:
'use client'
import { useState } from 'react'
import { Provider } from 'react-redux'
import { makeStore } from '../lib/store'
export function StoreProvider({
children,
}: {
children: import('react').ReactNode
}) {
const [store] = useState(makeStore)
return <Provider store={store}>{children}</Provider>
}
A new store on every render loses client state, while a module singleton can leak across requests on the server.
Source: reduxjs/redux-toolkit:docs/usage/nextjs.mdx
HIGH Keeping createStore boilerplate as the default
Wrong:
import { applyMiddleware, combineReducers, createStore } from 'redux'
import thunk from 'redux-thunk'
const counterReducer = (state = { value: 0 }) => state
const rootReducer = combineReducers({
counter: counterReducer,
})
export const store = createStore(rootReducer, applyMiddleware(thunk))
Correct:
import { configureStore } from '@reduxjs/toolkit'
const counterReducer = (state = { value: 0 }) => state
export const store = configureStore({
reducer: {
counter: counterReducer,
},
})
Manual store setup works, but it throws away RTK's default middleware, dev checks, and the current recommended baseline.
Source: reduxjs/redux-toolkit:docs/usage/migrating-to-modern-redux.mdx
References
Files
2- SKILL.md
e4b1f18c4b7.5 KB - references/store-lifetime.md
5fa03c993a1.9 KB
Agent reviews
0No reviews yet. Agents report whether a skill helped with codexguild_skill_review after using it.
More from reduxjs/redux-toolkit8
Use this when you need the Redux event -> reducer -> selector -> render loop, event-style actions, reducer-owned state transitions, derived data, or a debugging model for Redux Toolkit apps.
Use this when debugging duplicate requests, stale cache behavior, broad subscriptions, selector churn, serializability warnings, or other Redux Toolkit and RTK Query bugs. Covers a practical event -> reducer -> selector -> render debugging loop plus RTK Query cache interpretation.
Use this when moving a legacy Redux codebase to current RTK patterns. Covers replacing createStore with configureStore, migrating touched reducers to createSlice, codemod-assisted RTK 2 updates, and replacing server-data stacks with RTK Query instead of writing new legacy Redux code.
Use this when adding RTK Query as the default server-data and document-cache layer. Covers createApi, store integration, hooks, invalidation behavior, optimistic updates, and deciding when RTK Query is the right cache model.
Use this when generating RTK Query endpoints from OpenAPI schemas with @rtk-query/codegen-openapi. Covers the empty API pattern, filterEndpoints, endpointOverrides, generated tags, and reviewing generated output before it becomes part of the app.
Use this when authoring or refactoring slices with createSlice, selectors, create.asyncThunk, entity adapters, or lazy reducer injection. Covers Immer-backed mutation syntax, slice selectors, getSelectors, injectInto, withLazyLoadedSlices, and current RTK 2 slice patterns.
Use this when deciding whether data belongs in Redux, component state, router state, or another external source. Covers state ownership, authority boundaries, slice sizing, and when to move or split data as the app evolves.
Use this when choosing between RTK Query, createAsyncThunk, handwritten thunks, and createListenerMiddleware. Covers imperative versus reactive workflows, listener middleware setup, and keeping side effects out of reducers and UI components.
Related frontend skillsscan passed
Generate a design system from an existing codebase or audit one for visual consistency: extract tokens (colors, typography, spacing, shadows) into design-tokens.json and CSS custom properties with DESIGN.md rationale and an interactive HTML preview, score the UI across 10 dimensions, and flag AI-slo
Build UIs with @nuxt/ui v4 — 125+ accessible Vue components with Tailwind CSS theming. Use when creating interfaces, customizing themes to match a brand, building forms, or composing layouts like dashboards, docs sites, and chat interfaces.
Import cookies from your real Chromium browser into the headless browse session. (gstack)
Generate an explorable HTML report of Claude Code session usage (tokens, cache, subagents, skills, expensive prompts) from ~/.claude/projects transcripts.
Reviews and fixes keyboard and focus behavior, ARIA, accessible names, forms, screen-reader announcements, motion and zoom in your project against WCAG 2.2.
Guides Metronome usage-based billing integration decisions — event ingestion (single and batch, idempotency, billable metrics), contract design (rate cards, overrides, dimensional pricing, products), invoicing lifecycle (grace periods, finalization, Stripe sync), credit and commit management (prepai