What is Redux Toolkit?
State management is one of the most critical architectural decisions in React application development. As applications grow in complexity, managing state across components becomes increasingly challenging. Redux Toolkit has emerged as the official, recommended approach for writing Redux logic, providing a standardized way to build predictable state management layers.
Redux Toolkit (RTK) is the official, recommended approach for writing Redux logic. The @reduxjs/toolkit package wraps around the core redux package and contains API methods and common dependencies essential for building Redux applications. Redux Toolkit builds in suggested best practices, simplifies most Redux tasks, prevents common mistakes, and makes it easier to write Redux applications.
The core philosophy behind Redux Toolkit is to eliminate the "boilerplate" from hand-written Redux logic while preventing common errors. Traditional Redux required developers to write extensive action types, action creators, and reducers manually, often spreading code across multiple files. Redux Toolkit consolidates these patterns into focused APIs that handle the repetitive work automatically.
Whether you are a brand new Redux user setting up your first project, or an experienced user who wants to simplify an existing application, Redux Toolkit can help make your Redux code better. The package includes utilities that help simplify many common use cases, including store setup, creating reducers and writing immutable update logic, and creating entire "slices" of state at once.
For teams building modern React applications, understanding Redux Toolkit is essential for maintaining scalable, maintainable state management architecture. Combined with our React development services, adopting Redux Toolkit helps teams write cleaner, more predictable code. Additionally, our AI automation services can help integrate intelligent state management patterns into your applications.
Powerful abstractions that simplify Redux development
configureStore
Sets up a well-configured Redux store with a single function call, automatically handling middleware, DevTools, and reducer combination.
createSlice
Generates action creators and reducers automatically, using Immer for immutable updates with 'mutating' syntax.
createAsyncThunk
Abstracts async operation patterns, automatically dispatching pending, fulfilled, and rejected actions.
createEntityAdapter
Provides prebuilt reducers and selectors for CRUD operations on normalized state collections.
createSelector
Memoized selectors that prevent unnecessary recalculations for optimal performance.
RTK Query
Full data fetching and caching solution with auto-generated React hooks for API interactions.
The Evolution from Legacy Redux
Understanding the evolution from legacy Redux patterns helps appreciate why Redux Toolkit was created. The original Redux core is a very small and deliberately unopinionated library. It provides a few small API primitives including createStore to create a Redux store, combineReducers to combine multiple slice reducers, applyMiddleware to combine middleware, and compose to combine store enhancers.
Other than these core functions, all other Redux-related logic in an application had to be written entirely by developers. This meant reducers were typically written with switch statements and manual immutable updates using object spreads and array operations. It was also common to spread code across multiple files like actions/todos.js, constants/todos.js, and reducers/todos.js. Additionally, store setup usually required multiple steps to add commonly used middleware like thunks and enable Redux DevTools Extension support.
These patterns required a lot of verbose and repetitive code that was not strictly necessary to use Redux. The boilerplate code led to more opportunities to make mistakes, with accidental state mutation being the number one cause of Redux bugs.
Code Reduction Impact
A typical Redux feature that previously required multiple files and dozens of lines of code can now be written in a single file with clear, readable syntax. The reducer code is shorter, easier to understand, and it is much more clear what is actually being updated in each case. Development teams report significant reductions in boilerplate when migrating to Redux Toolkit, allowing them to focus on business logic rather than infrastructure code.
For teams comparing React frameworks, understanding how Redux Toolkit integrates with Next.js vs React can help inform architectural decisions for new projects. Similarly, improving React user experience through techniques like skeleton UI loading states complements effective state management for performant applications.
Migration Strategies
Migrating an existing Redux application to Redux Toolkit can be done incrementally. Here are the key strategies for successful migration.
Step 1: Replace createStore with configureStore
The easiest first step is to replace the legacy createStore with configureStore. This provides immediate benefits including automatic middleware setup, Redux DevTools configuration, and development-mode checks for mutations. The change is minimal but provides significant value immediately.
// Legacy approach
import { createStore, combineReducers } from 'redux';
import thunk from 'redux-thunk';
import { composeWithDevTools } from 'redux-devtools-extension';
const rootReducer = combineReducers({ todos, filters });
const store = createStore(
rootReducer,
composeWithDevTools(applyMiddleware(thunk))
);
// Redux Toolkit approach
import { configureStore } from '@reduxjs/toolkit';
import todoReducer from './features/todos/todoSlice';
import filterReducer from './features/filters/filterSlice';
export const store = configureStore({
reducer: {
todos: todoReducer,
filters: filterReducer
}
});
Step 2: Convert Reducers to Slices
For each feature, convert the legacy reducer pattern to use createSlice. This involves defining an initial state, writing reducer functions using Immer's "mutating" syntax, and exporting the generated actions and reducer.
Step 3: Migrate Async Logic
For async operations, consider migrating to createAsyncThunk or RTK Query. createAsyncThunk works well for simple async patterns, while RTK Query is ideal for server state and data fetching scenarios.
Step 4: Implement Entity Adapter
For collections of items, implement createEntityAdapter to normalize state structure. This improves performance and simplifies CRUD operations.
The migration can be done one slice at a time without affecting other parts of the application, allowing for gradual adoption. For teams working on complex React applications, our web development team can help guide you through the migration process.
1// LEGACY PATTERN2const ADD_TODO = 'ADD_TODO';3const TODO_TOGGLED = 'TODO_TOGGLED';4 5export const addTodo = text => ({6 type: ADD_TODO,7 payload: { id: nanoid(), text }8});9 10export const todoToggled = id => ({11 type: TODO_TOGGLED,12 payload: id13});14 15export const todosReducer = (state = [], action) => {16 switch (action.type) {17 case ADD_TODO:18 return state.concat({19 id: action.payload.id,20 text: action.payload.text,21 completed: false22 });23 case TODO_TOGGLED:24 return state.map(todo => {25 if (todo.id !== action.payload) return todo;26 return { ...todo, completed: !todo.completed };27 });28 default:29 return state;30 }31};32 33// REDUX TOOLKIT PATTERN34import { createSlice, nanoid } from '@reduxjs/toolkit';35 36const todosSlice = createSlice({37 name: 'todos',38 initialState: [],39 reducers: {40 todoAdded(state, action) {41 state.push({42 id: nanoid(),43 text: action.payload,44 completed: false45 });46 },47 todoToggled(state, action) {48 const todo = state.find(t => t.id === action.payload);49 if (todo) todo.completed = !todo.completed;50 }51 }52});53 54export const { todoAdded, todoToggled } = todosSlice.actions;55export default todosSlice.reducer;Why Adopt Redux Toolkit?
As Redux maintainers emphasize, the recommendation is clear: all Redux users should write their Redux code with Redux Toolkit because it simplifies code and eliminates many common Redux mistakes and bugs.
The "boilerplate" and complexity of early Redux patterns was never a necessary part of Redux. Those patterns only existed because the original Flux Architecture used similar approaches, the early Redux docs showed action type constants to enable separating code into different files by type, JavaScript is a mutable language by default requiring manual immutable updates, and Redux was originally built in just a few weeks and designed to be a minimal core.
Key Benefits
-
Simplified store setup to a single function call while retaining full configuration options. The
configureStoreAPI handles combining reducers, adding middleware, and setting up DevTools automatically. -
Accidental mutation elimination which has always been the number one cause of Redux bugs. The dev-mode middleware in
configureStorecatches accidental mutations during development, preventing hard-to-debug issues. -
No manual action creators or action types to write by hand. The
createSliceAPI generates these automatically based on your reducer names, eliminating repetitive code. -
Error-prone immutable update logic removed from the equation. Immer enables writing "mutating" syntax while producing immutable updates under the hood, making code more readable.
-
Feature code in one file instead of spreading across multiple files. Related state logic stays together, improving maintainability and reducing cognitive load.
-
Excellent TypeScript support with APIs designed for type safety. TypeScript integration helps catch errors early and improves developer experience.
-
RTK Query eliminates need for thunks, reducers, action creators for data fetching. The auto-generated hooks handle loading states, caching, and invalidation automatically.
The redux core package still works, but today it is considered obsolete. All of its APIs are also re-exported from @reduxjs/toolkit, and configureStore does everything createStore does but with better default behavior and configurability.
RTK Query: Data Fetching Solved
RTK Query is a full data fetching and caching solution for Redux apps, included as a separate optional @reduxjs/toolkit/query entry point. It lets you define endpoints (REST, GraphQL, or any async function) and generates a reducer and middleware that fully manage fetching data, updating loading state, and caching results.
Complete RTK Query Example
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
export const pokemonApi = createApi({
reducerPath: 'pokemonApi',
baseQuery: fetchBaseQuery({ baseUrl: 'https://pokeapi.co/api/v2/' }),
endpoints: (builder) => ({
getPokemonByName: builder.query({
query: (name) => `pokemon/${name}`,
}),
getPokemonSpecies: builder.query({
query: (id) => `pokemon-species/${id}`,
}),
}),
});
export const { useGetPokemonByNameQuery, useGetPokemonSpeciesQuery } = pokemonApi;
Auto-Generated Hooks
One of the most powerful features of RTK Query is that it automatically generates React hooks that can be used in components to fetch data:
const { data, isFetching } = useGetPokemonByNameQuery('pikachu');
This single line replaces what previously required:
- Thunks for async logic
- Reducers for loading and error states
- Action creators for API requests
- Effect hooks for subscriptions
RTK Query can eliminate the need to write any thunks, reducers, action creators, or effect hooks to manage fetching data and tracking loading state. For applications that need robust data fetching alongside client state management, RTK Query provides a unified solution that integrates seamlessly with your existing Redux store. This approach aligns well with our AI automation services for building intelligent, data-driven applications.
Best Practices
Following best practices ensures you get the maximum benefit from Redux Toolkit.
Organize by Feature
Structure your Redux code by feature rather than by type. Each feature should have its own directory containing the slice, selectors, and any related logic. This keeps related code together and makes the codebase easier to navigate and maintain.
src/
features/
todos/
todoSlice.js
todoSelectors.js
todoSlice.test.js
users/
userSlice.js
userSelectors.js
Use TypeScript
Redux Toolkit has excellent TypeScript support. Define types for your state, actions, and payloads to get compile-time safety and better developer experience. The APIs are designed to provide strong type inference out of the box.
Leverage DevTools
The Redux DevTools are powerful for debugging. Use them to inspect state changes, time-travel through action history, and identify mutations. The dev-mode middleware in configureStore helps catch common errors during development.
Consider RTK Query for Data Fetching
For any data fetching scenarios, seriously consider RTK Query before implementing custom solutions. RTK Query handles caching, loading states, and invalidation automatically, often eliminating the need for significant amounts of boilerplate code.
Keep Slices Focused
Each slice should represent a single feature domain. Avoid creating "god slices" that try to manage too much state. Smaller, focused slices are easier to maintain, test, and understand. If a slice grows too large, consider splitting it into multiple smaller slices that manage related concerns.
For teams building complex React applications, pairing these best practices with our React calendar component development expertise demonstrates how proper state management scales across features. Additionally, understanding React skeleton UI patterns helps create smooth user experiences that complement robust state management.
Performance Considerations
Redux Toolkit includes several features that contribute to application performance.
Memoized Selectors
The createSelector API creates memoized selectors that prevent unnecessary recalculations. Use memoized selectors for derived data that depends on state values to avoid expensive recalculations on every render.
import { createSelector } from '@reduxjs/toolkit';
const selectCompletedTodos = createSelector(
[(state) => state.todos],
(todos) => todos.filter(todo => todo.completed)
);
Normalized State with Entity Adapter
The createEntityAdapter promotes normalized state structures where each item is stored by ID. This avoids array searches and makes updates to individual items more efficient. The adapter provides selectors for getting entities by ID, getting all entities, and selecting IDs.
import { createEntityAdapter } from '@reduxjs/toolkit';
const todosAdapter = createEntityAdapter();
const todosSlice = createSlice({
name: 'todos',
initialState: todosAdapter.getInitialState(),
reducers: {
todoAdded: todosAdapter.addOne,
todoUpdated: todosAdapter.updateOne,
todoRemoved: todosAdapter.removeOne
}
});
RTK Query Caching
RTK Query provides automatic caching of API responses. The cache is keyed by arguments, so identical requests return cached data instantly. This reduces unnecessary network requests and improves perceived performance. Cache invalidation can be configured to refresh data when needed.
Immer Performance
While Immer enables "mutating" syntax, it produces immutable updates efficiently under the hood. The performance overhead is negligible for most applications, and the developer experience improvement typically outweighs any minor performance cost for most applications. For applications with extreme performance requirements, the generated selectors and normalized state structures help minimize unnecessary computations.
When building high-performance React applications, combining Redux Toolkit with our web development services ensures your state management architecture supports excellent user experiences.
Comparison with Alternatives
While Redux Toolkit is the recommended approach for Redux, the state management landscape includes several alternatives.
React Context and useReducer
For small applications or isolated features, React's built-in Context and useReducer may be sufficient. Context works well when updates are infrequent, but can cause unnecessary re-renders when overused. Redux Toolkit provides better performance characteristics for complex state logic with many selectors and derived state.
Zustand
Zustand is a lightweight alternative that provides a simpler API than Redux. It works well for medium-sized applications but may lack some of the advanced features and ecosystem of Redux Toolkit. For teams that want simpler state management without Redux's conventions, Zustand offers a viable alternative.
Jotai and Recoil
These atomic state management solutions offer different paradigms for managing state. They work well for applications where granular reactivity is important, but have different mental models than Redux. The atomic approach can be more intuitive for certain use cases but requires learning new patterns.
React Query and SWR
While RTK Query handles server state within Redux, standalone solutions like React Query and SWR focus specifically on data fetching. If you are already using Redux for client state, RTK Query provides a unified solution that integrates with your existing store. However, if your data fetching needs are separate from client state management, React Query may be worth considering.
When to Choose What
The choice depends on your specific requirements, application complexity, and team preferences. For applications already using Redux, adopting Redux Toolkit provides a consistent path forward that builds on existing knowledge. For new applications, consider the complexity of your state management needs and choose accordingly. Our web development services team can help evaluate the best approach for your specific use case. Additionally, our AI automation services can help integrate intelligent data fetching and caching strategies.
Frequently Asked Questions
Is Redux Toolkit backwards compatible with existing Redux code?
Yes, Redux Toolkit is designed to work with existing Redux code. You can migrate incrementally, starting with configureStore while keeping legacy reducers. The configureStore function accepts a reducer option that can include both new slices and existing reducers.
Do I need to rewrite all my existing Redux code to use Redux Toolkit?
No, you can migrate incrementally. Start by replacing createStore with configureStore, then gradually convert reducers to slices as you work on features. This allows you to realize benefits without massive rewrites.
Is RTK Query replacing Redux Toolkit or are they used together?
RTK Query is part of the Redux Toolkit package and is designed to work alongside other RTK APIs. It addresses data fetching specifically while other features use createSlice and configureStore for client state management.
How does Immer affect performance compared to manual immutable updates?
Immer produces efficient immutable updates under the hood. The developer experience improvement typically outweighs any minor performance cost for most applications. The performance overhead is negligible for typical use cases.
Should I use Redux Toolkit for small applications?
For very small applications with simple state needs, React's built-in Context and useReducer may be sufficient. Redux Toolkit shines in applications with complex, shared state requirements and multiple components that need to access and update the same state.
Does Redux Toolkit work with TypeScript?
Yes, Redux Toolkit has excellent TypeScript support. The APIs are designed to provide strong type inference and compile-time safety. TypeScript integration helps catch errors early and improves the developer experience.
Getting Started
To begin using Redux Toolkit in your React application, follow these steps to set up your first slice and store.
Installation
npm install @reduxjs/toolkit react-redux
# or
yarn add @reduxjs/toolkit react-redux
Create Your Store
import { configureStore } from '@reduxjs/toolkit';
import todoReducer from './features/todos/todoSlice';
import filterReducer from './features/filters/filterSlice';
export const store = configureStore({
reducer: {
todos: todoReducer,
filters: filterReducer
}
});
Wrap Your App
import { Provider } from 'react-redux';
import { store } from './store';
import App from './App';
function Root() {
return (
<Provider store={store}>
<App />
</Provider>
);
}
export default Root;
Create a Slice
import { createSlice } from '@reduxjs/toolkit';
export const counterSlice = createSlice({
name: 'counter',
initialState: { value: 0 },
reducers: {
increment: (state) => { state.value += 1; },
decrement: (state) => { state.value -= 1; },
incrementByAmount: (state, action) => {
state.value += action.payload;
}
}
});
export const { increment, decrement, incrementByAmount } = counterSlice.actions;
export const selectCount = (state) => state.counter.value;
export default counterSlice.reducer;
Use in Components
import { useSelector, useDispatch } from 'react-redux';
import { increment, selectCount } from './counterSlice';
function Counter() {
const count = useSelector(selectCount);
const dispatch = useDispatch();
return (
<div>
<p>Count: {count}</p>
<button onClick={() => dispatch(increment())}>Increment</button>
</div>
);
}
With these basics in place, you can start building features using slices and gradually migrate existing Redux code to leverage the full power of Redux Toolkit.
Conclusion
Redux Toolkit represents the modern, recommended way to write Redux applications. By eliminating boilerplate, preventing common mistakes, and providing powerful APIs like createSlice, configureStore, and RTK Query, it makes Redux development more productive and enjoyable.
Whether you are starting a new project or migrating an existing one, adopting Redux Toolkit provides immediate benefits and sets your application up for long-term success. The transition from legacy Redux patterns to Redux Toolkit can be gradual, allowing you to realize benefits without massive rewrites.
Start by replacing createStore with configureStore, then incrementally convert slices and migrate async logic. The investment in learning Redux Toolkit pays dividends in code quality, developer experience, and application maintainability.
For organizations looking to modernize their React applications, our team of experienced developers can help guide you through the adoption process. From initial assessment through implementation, we help teams leverage Redux Toolkit effectively while maintaining business continuity. Contact our web development team to discuss how we can support your state management modernization journey.