Using Redux Toolkit's createSlice to Cut Boilerplate While Keeping Predictable Updates
Learn how createSlice generates action creators and reducers, integrates with Immer, and works with DevTools—plus the trade‑offs to watch for.
19 Sept 2026, 15:18 UTC

The problem: Redux boilerplate slows you down
When you start a new feature with plain Redux, you write action type constants, action creator functions, and a reducer switch statement. Even a simple counter requires dozens of lines that are easy to mistype and hard to keep in sync. This overhead distracts from the actual logic you want to implement.
Thesis: createSlice removes the repetitive parts while preserving Redux’s guarantees
Redux Toolkit’s createSlice API generates action types and action creators from the reducer case reducers you provide. It also wraps those reducers with Immer, so you can write mutating code that is safely turned into immutable updates. The slice reducer plugs straight into combineReducers and works with the Redux DevTools extension out of the box.
Worked example: a todo slice
First, install the toolkit in your project (run in the project root):
# npm or yarn – no special permissions required
npm install @reduxjs/toolkit
# or
yarn add @reduxjs/toolkit
Create a file src/features/todos/todoSlice.js:
import { createSlice } from '@reduxjs/toolkit'
const todoSlice = createSlice({
name: 'todos',
initialState: {
items: [],
filter: 'all',
},
reducers: {
addTodo(state, action) {
// Immer lets us push directly; the update is immutable
state.items.push({ id: Date.now(), text: action.payload, completed: false })
},
toggleTodo(state, action) {
const todo = state.items.find(t => t.id === action.payload)
if (todo) todo.completed = !todo.completed
},
setFilter(state, action) {
state.filter = action.payload
},
},
})
export const { addTodo, toggleTodo, setFilter } = todoSlice.actions
export default todoSlice.reducer
Add the slice to your store:
import { configureStore } from '@reduxjs/toolkit'
import todoReducer from './features/todos/todoSlice'
const store = configureStore({
reducer: {
todos: todoReducer,
},
})
export default store
Use the generated actions in a component:
import { useDispatch, useSelector } from 'react-redux'
import { addTodo, toggleTodo, setFilter } from './features/todos/todoSlice'
function TodoApp() {
const dispatch = useDispatch()
const { items, filter } = useSelector(state => state.todos)
return (
{
if (e.key === 'Enter') {
dispatch(addTodo(e.target.value))
e.target.value = ''
}
}}
/>
dispatch(setFilter('active'))}>Active
{items
.filter(item => filter === 'all' || (filter === 'active' && !item.completed) || (filter === 'completed' && item.completed))
.map(item => (
- dispatch(toggleTodo(item.id))} style={{ textDecoration: item.completed ? 'line-through' : 'none' }}>
{item.text}
))}
)
}
How to verify it works
- Start your app (
npm start) and open the Redux DevTools extension. - Dispatch an action (e.g., type in the input and press Enter).
- In DevTools you should see an action type like
todos/addTodowith a payload equal to the typed text, and the state diff showing the new item added immutably. - Check the console:
console.log(store.getState())after each dispatch shows the updated state without mutations leaking.
Trade‑offs and limitations
- Bundle size: Each case reducer generates a distinct action type string. A slice with many cases can increase the final bundle. You can inspect this with a tool like
webpack-bundle-analyzerorsource-map-explorerafter a production build. - Immer dependency: The mutating syntax only works if Immer is installed (it comes with Redux Toolkit). If you accidentally remove Immer, direct mutations will corrupt state.
- Learning curve: Developers accustomed to writing action types by hand may need to adjust to the generated names (
sliceName/actionType).
To check the bundle impact, run:
# Assuming a Create React App setup npm run build npx source-map-explorer build/static/js/*.jsLook for the chunk containing your slice; if the size feels large, consider splitting the slice or moving rarely‑used reducers to separate slices.
Actionable closing
For new Redux features, start with
createSlice. It eliminates boilerplate, gives you immutable updates via Immer, and integrates seamlessly with DevTools. After adding a slice, verify the behavior in DevTools and run a bundle analysis to ensure the generated action types don’t bloat your output. If you hit the bundle‑size ceiling, split the slice or hand‑craft only the reducers that truly need custom logic.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.