Sitemap

Redux Toolkit: The Modern Way to Manage State in React

4 min readJun 28, 2025

--

State management is a core challenge in building scalable React applications. While classic Redux has long been a popular solution, it’s often criticized for its boilerplate and complexity. Redux Toolkit (RTK) is the official, recommended approach for writing Redux logic today. It dramatically simplifies Redux code, encourages best practices, and integrates seamlessly with modern React.

Why Redux Toolkit?

Classic Redux requires a lot of setup: action types, action creators, reducers, and store configuration. This can be overwhelming for beginners and tedious for experienced developers. Redux Toolkit solves these problems by:

  • Reducing boilerplate with sensible defaults and helper functions.
  • Encouraging best practices (like using Immer for immutable updates).
  • Providing utilities for common tasks (like async logic).
  • Making Redux code easier to read, write, and maintain.

Key Concepts in Redux Toolkit

Let’s break down the main building blocks of Redux Toolkit, with code samples and explanations.

1. Slices

A slice is a collection of Redux reducer logic and actions for a single feature of your app, defined in one file. Slices are created using ‎`createSlice`.

Example: Counter Slice

import { createSlice } from '@reduxjs/toolkit';

const initialState = { value: 0 };

const counterSlice = createSlice({
name: 'counter',
initialState,
reducers: {
increment(state) {
state.value++;
},
decrement(state) {
state.value--;
},
reset(state) {
state.value = 0;
},
incrementByAmount(state, action) {
state.value += Number(action.payload);
},
},
});

export const { increment, decrement, incrementByAmount, reset } = counterSlice.actions;
export default counterSlice.reducer;

What’s happening here?

  • ‎`createSlice` automatically generates action creators and action types.
  • Reducer functions can “mutate” state directly, thanks to Immer (under the hood, state is still immutable).
  • All logic for the counter feature is in one place.

2. Store

The store holds your application’s state and connects your slices. With Redux Toolkit, you use ‎`configureStore` to set up the store with minimal configuration.

Example: Store Setup

import { configureStore } from '@reduxjs/toolkit';
import counterReducer from '../features/counter/counterSlice';

const store = configureStore({
reducer: {
counter: counterReducer,
},
});

export default store;

Key points:

  • Each slice reducer is registered under a key (here, ‎`counter`).
  • You can add more slices as your app grows.

3. Actions and Reducers

Get Vinod Jatav’s stories in your inbox

Join Medium for free to get updates from this writer.

Actions and reducers are defined together in the slice. When you call an action creator (like ‎`increment()`), Redux dispatches an action, and the corresponding reducer updates the state.

Example Usage in a React Component:

import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { increment, decrement } from './features/counter/counterSlice';

export default function App() {
const count = useSelector((state) => state.counter.value);
const dispatch = useDispatch();

return (
<div>
<button onClick={() => dispatch(increment())}>+</button>
<span>Count: {count}</span>
<button onClick={() => dispatch(decrement())}>-</button>
</div>
);
}
  • ‎`useSelector` reads state from the store.
  • ‎`useDispatch` sends actions to the store.

4. Async Logic with createAsyncThunk

Handling async operations (like API calls) is easy with ‎`createAsyncThunk`. It generates action types and handles loading, success, and error states.

Example:

import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';

// Async thunk for fetching data
export const fetchData = createAsyncThunk(
'data/fetchData',
async (id, thunkAPI) => {
const response = await fetch(`/api/data/${id}`);
return response.json();
}
);

const dataSlice = createSlice({
name: 'data',
initialState: { value: null, status: 'idle', error: null },
reducers: {},
extraReducers: (builder) => {
builder
.addCase(fetchData.pending, (state) => {
state.status = 'loading';
})
.addCase(fetchData.fulfilled, (state, action) => {
state.status = 'succeeded';
state.value = action.payload;
})
.addCase(fetchData.rejected, (state, action) => {
state.status = 'failed';
state.error = action.error.message;
});
},
});
  • ‎`createAsyncThunk` handles the async flow.
  • ‎`extraReducers` lets you respond to actions generated by thunks.

Integrating Redux Toolkit into a React Project

Step 1: Install dependencies

npm install @reduxjs/toolkit react-redux

Step 2: Create your slice(s)
Organize each feature in its own folder with a slice file.

Step 3: Configure the store
Combine your slices in ‎`store.js` using ‎`configureStore`.

Step 4: Provide the store to React
Wrap your app in a ‎`<Provider>` at the root:

import React from 'react';
import { createRoot } from 'react-dom/client';
import App from './App';
import { Provider } from 'react-redux';
import store from './redux/store';

const rootElement = document.getElementById('root');
const root = createRoot(rootElement);

root.render(
<Provider store={store}>
<App />
</Provider>
);

Step 5: Use Redux state and actions in components
Use ‎`useSelector` and ‎`useDispatch` from ‎`react-redux`.

Practical Tips

  • Keep slices focused: One slice per feature (e.g., ‎`counterSlice`, ‎`userSlice`).
  • Use Immer-powered reducers: Mutate state directly in reducers — Redux Toolkit handles immutability.
  • Leverage ‎`createAsyncThunk` for async logic: It simplifies API calls and async flows.
  • Type your state (with TypeScript): For larger projects, TypeScript + Redux Toolkit is a great combo.
  • Use Redux DevTools: ‎`configureStore` enables them by default for easy debugging.

Conclusion

Redux Toolkit is the modern, recommended way to use Redux in React. It reduces boilerplate, encourages best practices, and makes state management approachable for all React developers. By organizing your logic with slices, using the powerful store setup, and handling async with thunks, you can build scalable, maintainable apps with ease.

--

--

Vinod Jatav
Vinod Jatav

Written by Vinod Jatav

Software Developer Engineer at Siemens