Redux Toolkit 01 ⚛️ Installation — Package setup & store configuration
Redux Toolkit is the official, recommended way to write Redux logic. It is not a separate state management library; it is a set of utilities that wrap the Redux core, eliminating the boilerplate that made classic Redux painful to set up. The configureStore function replaces the manual construction of a store with a single call that combines reducers, adds the thunk middleware for async logic, enables Redux DevTools, and installs development-time checks for accidental state mutations . Before any slice or component can read from the store, two pieces must be in place: the packages, and a configured store provided to the React tree.
This chapter covers the installation of @reduxjs/toolkit and react-redux, the creation of the store with configureStore, the wrapping of the application in the React-Redux Provider, and the TypeScript type inference that makes RootState and AppDispatch available throughout the codebase. It establishes the foundation for every subsequent Redux Toolkit chapter: slices, async thunks, and RTK Query all depend on the store created here.
Key point: configureStore is not just a convenience wrapper. It enforces the Redux Toolkit defaults: thunk middleware is included automatically, development builds include mutation and serializability checks, and the DevTools extension is enabled without additional configuration. The reducer parameter accepts either a single root reducer or an object of slice reducers, in which case configureStore calls combineReducers internally .
Why Redux Toolkit replaces manual store setup
The boilerplate problem.
Classic Redux required the developer to import createStore, combineReducers, and applyMiddleware, assemble the root reducer manually, add thunk middleware explicitly, and wire up the DevTools enhancer if desired. Each of these steps was a place to make a mistake, and the resulting code obscured the actual application logic. configureStore collapses this into a single function call with sensible defaults. The store is created correctly without the developer needing to know which middleware to install or how to compose enhancers .
The middleware-defaults problem.
Redux Toolkit’s configureStore includes redux-thunk by default. This means async logic—functions dispatched to the store that can perform side effects and dispatch multiple actions—works immediately. In classic Redux, thunk had to be installed and applied manually. The Toolkit also adds development-only middleware that warns about accidental state mutations and non-serializable values in actions or state. These warnings catch the most common Redux bugs before they become production issues .
The TypeScript inference problem.
The RootState type is inferred from the store itself: ReturnType<typeof store.getState>. The AppDispatch type is inferred from typeof store.dispatch. Because these types are derived from the store’s actual configuration, they update automatically when slices are added or middleware is changed. There is no separate type declaration to maintain. The recommended pattern is to export these types from the store file and use them in typed hooks defined in a separate file, avoiding circular imports .
a. Installing the packages
Redux Toolkit requires two packages: @reduxjs/toolkit for the store, slices, and thunks, and react-redux for the Provider component and the useSelector/useDispatch hooks.
# Install both packages with npm
npm install @reduxjs/toolkit react-redux
# Or with yarn
yarn add @reduxjs/toolkit react-redux
# Or with pnpm
pnpm add @reduxjs/toolkit react-redux
The @reduxjs/toolkit package depends on Immer for immutable update logic inside reducers, and on Redux core. The react-redux package provides the React bindings. Neither package requires additional configuration after installation; the store setup is where they are connected .
b. Creating the store with configureStore
The store is created in a dedicated file, typically src/app/store.ts. The configureStore function accepts a configuration object. The only required field is reducer.
// src/app/store.ts
import { configureStore } from '@reduxjs/toolkit'
// The store is created with an empty reducer object initially
// Slices are added to this object as the application grows
export const store = configureStore({
reducer: {},
})
// Infer the RootState and AppDispatch types from the store itself
// These types update automatically when slices are added
export type RootState = ReturnType<typeof store.getState>
export type AppDispatch = typeof store.dispatch
When reducer is an object of slice reducers, configureStore passes it to combineReducers and produces a root reducer. When a slice is created later, its reducer is added to this object under the slice’s name. The RootState type then automatically includes that slice’s state shape .
// Example after adding a counter slice
import counterReducer from '../features/counter/counterSlice'
export const store = configureStore({
reducer: {
counter: counterReducer, // state.counter is now typed
},
})
The configureStore call also enables Redux DevTools automatically. In development, it adds middleware that checks for accidental mutations and non-serializable values. These checks do not exist in production builds, so there is no performance cost in the shipped application .
c. Providing the store to React
The store must be passed to the React-Redux Provider component, which is placed at the root of the component tree. Every component below the provider can then access the store through useSelector and useDispatch.
// src/main.tsx or src/index.tsx
import React from 'react'
import { createRoot } from 'react-dom/client'
import { Provider } from 'react-redux'
import { store } from './app/store'
import App from './App'
const root = createRoot(document.getElementById('root')!)
root.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
)
The Provider is not optional. It uses React context to distribute the store reference, and the hooks (useSelector, useDispatch) read that reference. Without the provider, the hooks throw an error because they cannot find the store context . The store is created once at module scope and passed as a prop; it is not recreated on re-renders.
For TypeScript projects, the recommended pattern is to define typed versions of the hooks in a separate file. This avoids importing RootState into every component and ensures that dispatch knows about thunks.
// src/app/hooks.ts
import { useDispatch, useSelector } from 'react-redux'
import type { RootState, AppDispatch } from './store'
// Use these throughout the app instead of the plain hooks
export const useAppDispatch = useDispatch.withTypes<AppDispatch>()
export const useAppSelector = useSelector.withTypes<RootState>()
The withTypes method is available in React-Redux v9 and later. It returns a hook that is pre-typed with the store’s state and dispatch types, so components do not need to annotate state: RootState on every selector call .
Complete Example Session
// ============================================
// PART 1: INSTALL THE PACKAGES
// ============================================
// Run in terminal:
// npm install @reduxjs/toolkit react-redux
// ============================================
// PART 2: CREATE THE STORE FILE
// ============================================
// src/app/store.ts
import { configureStore } from '@reduxjs/toolkit'
// ============================================
// PART 3: CONFIGURE THE STORE
// ============================================
// The reducer object will hold slice reducers
// Initially empty; slices are added later
export const store = configureStore({
reducer: {},
})
// ============================================
// PART 4: INFER TYPES FROM THE STORE
// ============================================
// RootState is the type of the entire state tree
// AppDispatch is the type of the dispatch function, including thunk support
export type RootState = ReturnType<typeof store.getState>
export type AppDispatch = typeof store.dispatch
// ============================================
// PART 5: CREATE TYPED HOOKS
// ============================================
// src/app/hooks.ts
// import { useDispatch, useSelector } from 'react-redux'
// import type { RootState, AppDispatch } from './store'
//
// export const useAppDispatch = useDispatch.withTypes<AppDispatch>()
// export const useAppSelector = useSelector.withTypes<RootState>()
// ============================================
// PART 6: WRAP THE APPLICATION IN THE PROVIDER
// ============================================
// src/main.tsx
// import React from 'react'
// import { createRoot } from 'react-dom/client'
// import { Provider } from 'react-redux'
// import { store } from './app/store'
// import App from './App'
//
// const root = createRoot(document.getElementById('root')!)
// root.render(
// <React.StrictMode>
// <Provider store={store}>
// <App />
// </Provider>
// </React.StrictMode>
// )
// ============================================
// PART 7: VERIFY WITH A COMPONENT
// ============================================
// A component below the provider can read state
// With an empty reducer object, state is {}
// After adding a slice, state.counter.value becomes accessible
// ============================================
// PART 8: ADD A SLICE REDUCER
// ============================================
// When a slice exists, import its reducer and add it to the store
// import counterReducer from '../features/counter/counterSlice'
//
// export const store = configureStore({
// reducer: {
// counter: counterReducer,
// },
// })
The eight parts demonstrate the complete setup: install, create the store, configure it, infer types, define typed hooks, wrap the application, and extend the store with a slice. The store is created once and never recreated. The provider passes it to the component tree, and the typed hooks ensure that components read the correct state shape.
Quick Reference
Installation Commands
| Package | Command | Purpose |
|---|---|---|
| Redux Toolkit | npm install @reduxjs/toolkit | Store, slices, thunks |
| React-Redux | npm install react-redux | Provider, hooks |
Store Configuration
| Option | Required | Default |
|---|---|---|
reducer | Yes | — |
middleware | No | Includes thunk and dev checks |
devTools | No | true |
preloadedState | No | undefined |
Type Inference
| Type | Derivation | Location |
|---|---|---|
RootState | ReturnType<typeof store.getState> | store.ts |
AppDispatch | typeof store.dispatch | store.ts |
| Typed hooks | useDispatch.withTypes<AppDispatch>() | hooks.ts |
Best Practices
✅ Do This:
// Create the store once at module scope
export const store = configureStore({ reducer: {} }) // ✅
// Infer RootState and AppDispatch from the store
export type RootState = ReturnType<typeof store.getState> // ✅
// Define typed hooks in a separate file
export const useAppDispatch = useDispatch.withTypes<AppDispatch>() // ✅
// Wrap the root component in Provider
<Provider store={store}><App /></Provider> // ✅
// Add slice reducers to the reducer object
reducer: { counter: counterReducer } // ✅
❌ Don’t Do This:
// Create the store inside a component
function App() { const store = configureStore({...}) } // ❌
// Skip the Provider
root.render(<App />) // Hooks throw // ❌
// Use plain useDispatch when thunks are dispatched
const dispatch = useDispatch() // Does not know thunks // ❌
// Manually type the state in every selector
useSelector((state: RootState) => state.counter.value) // ❌ (use typed hook)
// Omit the reducer object
configureStore({}) // Error: reducer is required // ❌
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
| “Could not find store” error | Provider missing from tree | Wrap root in <Provider store={store}> |
| Thunk dispatch type error | Using plain useDispatch | Use useAppDispatch from hooks.ts |
| Store recreated on every render | configureStore inside component | Move to module scope |
RootState empty | No slices added to reducer | Add slice reducers to the object |
| DevTools not working | Extension not installed | Install Redux DevTools browser extension |
Real-World Examples
1. Install packages
npm install @reduxjs/toolkit react-redux
2. Create store with empty reducer
export const store = configureStore({ reducer: {} })
3. Create store with a slice
export const store = configureStore({ reducer: { counter: counterReducer } })
4. Infer RootState
export type RootState = ReturnType<typeof store.getState>
5. Infer AppDispatch
export type AppDispatch = typeof store.dispatch
6. Typed useSelector
export const useAppSelector = useSelector.withTypes<RootState>()
7. Typed useDispatch
export const useAppDispatch = useDispatch.withTypes<AppDispatch>()
8. Wrap with Provider
<Provider store={store}><App /></Provider>
9. Verify store in component
const count = useAppSelector((state) => state.counter.value)
10. Check store state
console.log(store.getState())
Visual
The Setup Architecture
┌──────────────────────────────────────────────────────────────┐
│ STORE → PROVIDER → COMPONENTS │
│ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ MODULE SCOPE (store.ts) │ │
│ │ │ │
│ │ const store = configureStore({ reducer: {} }) │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌──────────────────────────────────────────────────┐ │ │
│ │ │ Store Object │ │ │
│ │ │ ├─ State: {} │ │ │
│ │ │ ├─ Dispatch: function │ │ │
│ │ │ ├─ Thunk middleware (default) │ │ │
│ │ │ └─ DevTools enabled (default) │ │ │
│ │ └──────────────────────────────────────────────────┘ │ │
│ └────────────────────────────────────────────────────────┘ │
│ │ │
│ │ passed as prop │
│ ▼ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ REACT ROOT (main.tsx) │ │
│ │ │ │
│ │ <Provider store={store}> │ │
│ │ <App /> │ │
│ │ </Provider> │ │
│ └────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ COMPONENTS │ │
│ │ │ │
│ │ useAppSelector((state) => state.something) │ │
│ │ useAppDispatch() │ │
│ │ │ │
│ │ Both hooks read from the Provider's context. │ │
│ └────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────┘
configureStore Defaults
┌──────────────────────────────────────────────────────────────┐
│ WHAT configureStore DOES AUTOMATICALLY │
│ │
│ configureStore({ reducer: { ... } }) │
│ │ │
│ ├── combineReducers(reducerObject) │
│ │ → root reducer │
│ │ │
│ ├── Adds thunk middleware │
│ │ → async actions work immediately │
│ │ │
│ ├── Adds dev-mode checks (development only) │
│ │ → mutation warnings │
│ │ → serializability warnings │
│ │ │
│ └── Enables Redux DevTools │
│ → browser extension connection │
│ │
│ No manual applyMiddleware, no compose, no enhancer wiring. │
└──────────────────────────────────────────────────────────────┘
Type Inference Flow
┌──────────────────────────────────────────────────────────────┐
│ RootState AND AppDispatch ARE DERIVED FROM THE STORE │
│ │
│ const store = configureStore({ reducer: { ... } }) │
│ │ │
│ ├── store.getState() → { counter: { value: number } } │
│ │ │ │
│ │ └── ReturnType<typeof store.getState> │
│ │ = RootState │
│ │ │
│ └── store.dispatch → ThunkDispatch & Dispatch │
│ │ │
│ └── typeof store.dispatch │
│ = AppDispatch │
│ │
│ When a new slice is added, RootState updates automatically. │
│ No manual type declaration is needed. │
└──────────────────────────────────────────────────────────────┘
Summary
| Item | Value |
|---|---|
| Packages | @reduxjs/toolkit, react-redux |
| Store creation | configureStore({ reducer }) |
| Required option | reducer (object or function) |
| Default middleware | redux-thunk + dev checks |
| DevTools | Enabled by default |
| Provider | <Provider store={store}> at root |
RootState | ReturnType<typeof store.getState> |
AppDispatch | typeof store.dispatch |
| Typed hooks file | useDispatch.withTypes<AppDispatch>() |
Key takeaways:
configureStoreis the only store setup needed. It combines reducers, adds thunk middleware, enables DevTools, and installs development-time checks. The developer does not need to callapplyMiddlewareorcombineReducersmanually .- The
reducerobject maps slice names to slice reducers. When a slice is created withcreateSlice, its reducer is imported and added to this object. The state shape is the combination of all slice states under their respective keys . RootStateandAppDispatchare inferred, not declared. They are derived from the store’s actual type. Adding a new slice to thereducerobject automatically updatesRootStatewithout any manual type change .- The
Provideris required for React components. Without it,useSelectoranduseDispatchthrow. The store is passed as a prop, and the provider distributes it through context . - Typed hooks belong in a separate file. Defining
useAppDispatchanduseAppSelectorinhooks.tsavoids circular imports and ensures that thunks are correctly typed when dispatched . - Redux DevTools is enabled automatically. The browser extension shows the action history, state tree, and time-travel debugging without any store configuration .
Remember: Redux Toolkit’s installation is two packages, and its store setup is one function call. The configureStore function does what classic Redux required five separate steps to accomplish: combine reducers, add middleware, enable DevTools, install development checks, and create the store. The store is created once at module scope, passed to the Provider at the root of the React tree, and accessed through typed hooks. Get the store and provider right, and every slice, thunk, and component subscription that follows has a solid foundation.
Stop using slow, ad-bloated tool sites! 🤮
🔎 Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)
⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free
🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!