Introduction
Redux Toolkit (RTK) is what the Redux team wishes they had in 2015. It replaces hand-written action types, switch-statement reducers, and thunks boilerplate with createSlice, configureStore, and createAsyncThunk. Immer is bundled so you can write mutating-looking reducers that are actually immutable.
With over 11,000 GitHub stars (counting only the toolkit repo — full Redux ecosystem has 60K+), RTK is now the officially recommended way to write Redux. The Redux docs actively discourage the old patterns.
What Redux Toolkit Does
RTK wraps Redux with better defaults: createSlice builds action creators and a reducer together, configureStore sets up middleware (redux-thunk, devtools, serializability checks), createAsyncThunk handles loading/error/success states for async calls, and RTK Query provides React Query-style data fetching with built-in caching.
Architecture Overview
createSlice createAsyncThunk
| |
reducers + actions pending/fulfilled/rejected actions
| |
combineReducers()
|
configureStore()
+ thunk
+ devtools
+ serializability checks
|
Provider + hooks
useSelector, useDispatch
|
RTK Query (optional)
cache + invalidation like React QuerySelf-Hosting & Configuration
// RTK Query: API caching with zero boilerplate
import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";
export const api = createApi({
reducerPath: "api",
baseQuery: fetchBaseQuery({ baseUrl: "/api" }),
tagTypes: ["Post"],
endpoints: (b) => ({
listPosts: b.query<Post[], void>({ query: () => "posts", providesTags: ["Post"] }),
getPost: b.query<Post, number>({ query: (id) => `posts/${id}` }),
createPost: b.mutation<Post, Partial<Post>>({
query: (body) => ({ url: "posts", method: "POST", body }),
invalidatesTags: ["Post"],
}),
}),
});
export const { useListPostsQuery, useGetPostQuery, useCreatePostMutation } = api;
// In store
import { configureStore } from "@reduxjs/toolkit";
export const store = configureStore({
reducer: { [api.reducerPath]: api.reducer /*, counter: counterReducer */ },
middleware: (gdm) => gdm().concat(api.middleware),
});Key Features
- createSlice — actions + reducer in one call
- Immer integration — write mutations, get immutability
- configureStore — thunks, devtools, checks all pre-wired
- createAsyncThunk — structured async flows with lifecycle actions
- RTK Query — data fetching and caching (React Query-like)
- createEntityAdapter — normalized collections with CRUD selectors
- TypeScript — excellent type inference from slice definitions
- Listener middleware — effects triggered by actions (redux-saga alternative)
Comparison with Similar Tools
| Feature | Redux Toolkit | Zustand | Jotai | MobX | Vanilla Redux |
|---|---|---|---|---|---|
| Boilerplate | Low | Very Low | Very Low | Low | High |
| Dev tools | Best in class | Decent | Via plugin | Decent | Best in class |
| Async | createAsyncThunk / RTK Query | Manual | Native async atoms | Reactions | Thunks/sagas |
| Learning curve | Moderate | Very Low | Very Low | Moderate | High |
| Opinionated | Yes | No | No | No | No |
| Best For | Large apps, Redux shops | Simple global state | Granular state | Reactive data | Legacy only |
FAQ
Q: Is Redux still relevant? A: Yes, especially for large apps that benefit from time-travel DevTools, middleware for complex side effects, or strict action auditability. For simpler needs, Zustand/Jotai are lighter.
Q: Should new projects still use Redux? A: If you choose Redux, always use Redux Toolkit — never vanilla Redux. For smaller apps, evaluate Zustand/Jotai first.
Q: RTK Query vs React Query? A: Both are great. Use RTK Query if you're already on Redux (integrates with your store, single cache). Use React Query/TanStack Query for non-Redux apps.
Q: Can I migrate from vanilla Redux to RTK incrementally?
A: Yes. configureStore accepts any reducers. Rewrite slices one at a time while keeping old reducers during the migration.
Sources
- GitHub: https://github.com/reduxjs/redux-toolkit
- Docs: https://redux-toolkit.js.org
- Maintainer: Redux team
- License: MIT