Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

State Management with Redux Toolkit

By Kokil Thapa | Last reviewed: September 2026

State Management with Redux Toolkit solves a problem every growing React front end hits: props drill through five layers, duplicate fetch logic in three components, and race conditions when two tabs update the same cart. Classic Redux fixed predictability but punished you with ceremony. Custom software projects that pair a Laravel or Symfony API with a React dashboard still need a disciplined client store when screens share auth, filters, and cached lists. Redux Toolkit (RTK) is the official, opinionated layer on top of Redux. It ships createSlice, Immer-powered reducers, async thunks, and RTK Query for data fetching. This guide walks through a store you can ship in production—not a toy counter app.

What Is State Management with Redux Toolkit and When Do You Need It?

Redux Toolkit is the recommended way to write Redux logic in 2026. The core Redux library still provides the store, subscriptions, and middleware pipeline. RTK removes the repetitive parts.

You reach for global state when multiple distant components read or write the same data. Auth session, shopping cart, UI theme, and cross-page filters are typical cases. Local useState stays fine for form fields and toggles that never leave one component.

On full-stack builds I deliver, the split is familiar. Server truth lives in Laravel 13 or a REST API. The React shell holds session tokens, optimistic UI flags, and cached list pages. That boundary keeps your backend authoritative while the front end stays responsive. If you mainly work in Vue, the mental model parallels Pinia versus Vuex 4—colocated stores, less boilerplate, DevTools support.

Redux Toolkit Store ArchitectureReact UIComponents + hooksRedux StoreSlices + RTK QueryREST APILaravel / NodeMiddleware Pipelineredux-thunk · RTK Query · DevToolsSerializable check in development
State Management with Redux Toolkit: UI dispatches actions, middleware handles async work, and slices hold normalized client state.

Skip Redux when your app is mostly static pages or a thin wrapper around server-rendered HTML. A brochure site built with WordPress development rarely needs a client store. Skip it when React Context plus useReducer covers two or three shared values and no complex async flows exist.

How Do You Set Up a Redux Toolkit Store in a React Project?

Start with a current React toolchain. Node.js 26 LTS and npm 12 are sensible defaults in 2026. Vite 8.x scaffolds faster than legacy Create React App.

Install dependencies

npm create vite@latest my-dashboard -- --template react-ts
cd my-dashboard
npm install @reduxjs/toolkit react-redux
npm install

Create the store entry point

Centralize configuration in src/app/store.ts. One file exports the store type and hooks.

import { configureStore } from '@reduxjs/toolkit';
import authReducer from '../features/auth/authSlice';
import cartReducer from '../features/cart/cartSlice';

export const store = configureStore({
  reducer: {
    auth: authReducer,
    cart: cartReducer,
  },
  devTools: import.meta.env.DEV,
});

export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;

Wire the Provider

Wrap your root component once. Every child can then use typed hooks.

import { Provider } from 'react-redux';
import { store } from './app/store';

createRoot(document.getElementById('root')!).render(
  <Provider store={store}>
    <App />
  </Provider>
);

Export typed hooks

Never import raw useDispatch and useSelector all over the codebase. Wrap them.

import { useDispatch, useSelector } from 'react-redux';
import type { RootState, AppDispatch } from '../app/store';

export const useAppDispatch = useDispatch.withTypes<AppDispatch>();
export const useAppSelector = useSelector.withTypes<RootState>();

That pattern gives autocomplete for slice names and action payloads. It catches typos at compile time instead of in production.

  1. Run the Vite scaffold with TypeScript.
  2. Add @reduxjs/toolkit and react-redux.
  3. Define configureStore with feature reducers.
  4. Wrap the tree in <Provider>.
  5. Expose typed hooks and use them everywhere.

How Do createSlice and createAsyncThunk Handle State Logic?

createSlice generates action creators and a reducer from a name, initial state, and reducer functions. Immer runs under the hood. You can write mutating-looking code that stays immutable.

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

type CartItem = { id: string; qty: number };

type CartState = {
  items: CartItem[];
  status: 'idle' | 'loading' | 'failed';
};

const initialState: CartState = { items: [], status: 'idle' };

const cartSlice = createSlice({
  name: 'cart',
  initialState,
  reducers: {
    addItem(state, action: PayloadAction<CartItem>) {
      const existing = state.items.find(i => i.id === action.payload.id);
      if (existing) {
        existing.qty += action.payload.qty;
      } else {
        state.items.push(action.payload);
      }
    },
    clearCart(state) {
      state.items = [];
    },
  },
});

export const { addItem, clearCart } = cartSlice.actions;
export default cartSlice.reducer;

Async work belongs in createAsyncThunk or RTK Query. Thunks fit custom orchestration—retry logic, multi-step flows, or writing to several slices after one response.

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

export const fetchProfile = createAsyncThunk(
  'auth/fetchProfile',
  async (_, { rejectWithValue }) => {
    const res = await fetch('/api/me', { credentials: 'include' });
    if (!res.ok) return rejectWithValue(await res.text());
    return res.json();
  }
);

Handle lifecycle states in extraReducers. Pending, fulfilled, and rejected cases stay colocated with the slice they mutate.

createAsyncThunk LifecycleDispatchfetchProfile()Pendingstatus loadingFulfilleddata in stateRejectederror storedextraReducers builder.addCase(pending) .addCase(fulfilled) .addCase(rejected)Keep side effects out of components
createAsyncThunk dispatches pending, fulfilled, and rejected actions that extraReducers handle in one slice file.

A common mistake is stuffing fetch calls inside components. Move them to thunks or RTK Query endpoints. Components should dispatch intent, not manage HTTP details.

Should You Use RTK Query Instead of Manual Fetch Logic?

RTK Query ships with Redux Toolkit. It generates hooks for queries and mutations. It caches responses, deduplicates in-flight requests, and refetches on focus or interval when you configure it.

import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';

export const api = createApi({
  reducerPath: 'api',
  baseQuery: fetchBaseQuery({
    baseUrl: '/api',
    prepareHeaders: (headers, { getState }) => {
      const token = (getState() as RootState).auth.token;
      if (token) headers.set('Authorization', `Bearer ${token}`);
    },
  }),
  tagTypes: ['Order', 'Product'],
  endpoints: (builder) => ({
    getOrders: builder.query<Order[], void>({
      query: () => 'orders',
      providesTags: ['Order'],
    }),
    createOrder: builder.mutation<Order, Partial<Order>>({
      query: (body) => ({ url: 'orders', method: 'POST', body }),
      invalidatesTags: ['Order'],
    }),
  }),
});

export const { useGetOrdersQuery, useCreateOrderMutation } = api;

Register the API reducer and middleware in the store.

export const store = configureStore({
  reducer: {
    auth: authReducer,
    [api.reducerPath]: api.reducer,
  },
  middleware: (getDefault) => getDefault().concat(api.middleware),
});

For eCommerce dashboards—order lists, inventory panels, fulfilment boards—RTK Query saves weeks of cache bookkeeping. I have seen the same pattern on Laravel eCommerce admin panels where the API is stable and list endpoints dominate the UI.

ApproachBest forTrade-off
Local useStateSingle-component UI stateNo shared access across routes
React ContextTheme, locale, simple auth flagRe-renders grow with consumer count
createSlice + thunksCustom async flows, multi-slice updatesYou own cache invalidation
RTK QueryCRUD against REST or GraphQLLearning curve for tags and transforms
Redux Toolkit full storeLarge apps, DevTools, time-travel debugBundle size and setup overhead

The official Redux docs recommend RTK Query for server state and slices for client-only state. That split keeps your store readable six months later.

How Do You Structure Folders and Test Redux Toolkit Code?

Organize by feature, not by technical type. A features/cart/ folder holds the slice, selectors, components, and tests. Avoid a global reducers/ dump that nobody owns.

  • src/app/ — store, root hooks, global types.
  • src/features/<name>/ — slice, API endpoints, UI tied to that domain.
  • src/shared/ — dumb components and utilities with no store imports.

Selectors belong in the feature folder. Memoized selectors via createSelector prevent unnecessary re-renders when unrelated slice fields change.

import { createSelector } from '@reduxjs/toolkit';
import type { RootState } from '../../app/store';

const selectCart = (state: RootState) => state.cart.items;

export const selectCartTotal = createSelector(selectCart, (items) =>
  items.reduce((sum, item) => sum + item.qty * item.price, 0)
);

Testing slices and thunks

Test reducers as pure functions. Dispatch actions and assert the next state. No DOM required.

import cartReducer, { addItem } from './cartSlice';

test('addItem increments quantity for existing product', () => {
  const state = cartReducer(
    { items: [{ id: '1', qty: 2, price: 10 }], status: 'idle' },
    addItem({ id: '1', qty: 3, price: 10 })
  );
  expect(state.items[0].qty).toBe(5);
});

Mock fetch for thunk tests. Assert pending and fulfilled action sequences. For RTK Query, use the library's test utilities or MSW to stub HTTP at the network boundary.

Pair unit tests with integration checks before release. A pass through testing and optimization catches selector regressions that unit files miss. Use the JSON formatter when debugging API payloads returned into your slices.

Feature-First Folder Layoutsrc/app/store.ts + hooks.tsfeatures/authauthSlice.tsselectors.tsLoginForm.tsxfeatures/cartcartSlice.tsCartPanel.tsxcart.test.tsfeatures/apiapiSlice.tsRTK Queryendpoints
Colocate slices, selectors, and UI per feature so State Management with Redux Toolkit scales with team size.

What Production Pitfalls Break Redux Toolkit Apps?

Non-serializable values in state trigger development warnings from the default middleware. Dates, class instances, and DOM nodes belong outside the store—or convert them at the boundary.

Storing entire API responses without normalization duplicates nested entities. When one product name changes, three list views show three different strings. Normalize by ID in the slice or let RTK Query handle entity adapters.

Over-using global state is the silent killer. Not every modal open flag needs Redux. Reach for local state first. Promote to a slice only when a second route or distant component genuinely needs the same value.

Bundle size matters on mobile networks in Nepal and elsewhere. Code-split routes with React.lazy. Import only the hooks you use from RTK Query generated APIs. Tree-shaking helps, but lazy routes help more on admin dashboards with dozens of screens.

Keep secrets out of the store. JWT refresh tokens in memory are acceptable for SPAs. Never persist tokens to localStorage without threat modelling XSS. Pair front-end auth with a hardened API layer—patterns I apply on API development projects using Sanctum or Passport on Laravel.

When to Adopt Redux ToolkitShared across routes?No → useStateYes → next checkComplex async?RTK QueryServer cacheRedux ToolkitSlices + thunksContext APISimple shared vals
Use this decision flow before adopting State Management with Redux Toolkit on a new React screen or product.

Reference the official guides when upgrading major versions. The Redux Toolkit usage guide and React state management docs stay current with recommended patterns. Redux DevTools remain essential for tracing action order during bug hunts.

If your product mixes React admin UI with a PHP monolith, treat the store as a thin client cache. Business rules stay on the server. That mirrors how I structure enterprise applications where Laravel validates every mutation regardless of what the UI optimistically shows.

Key Takeaways

  • Use configureStore, typed hooks, and feature folders as your default State Management with Redux Toolkit baseline.
  • Keep server data in RTK Query and client-only UI state in slices—do not merge both into one blob.
  • Colocate createAsyncThunk lifecycle handlers in extraReducers instead of scattering fetch logic in components.
  • Normalize entities by ID and memoize selectors to avoid stale lists and excess re-renders.
  • Test reducers as pure functions; stub HTTP for thunks and RTK Query endpoints.
  • Reach for local state or Context first—promote to Redux only when sharing or DevTools debugging truly justify the overhead.

People Also Ask

Is Redux Toolkit still worth using in 2026?

Yes, for medium and large React apps that share complex client state or need RTK Query's cached data layer. Small apps and mostly static sites should stay with local state or Context. RTK remains the documented standard for Redux projects and integrates cleanly with TypeScript and Vite 8.x toolchains.

What is the difference between Redux and Redux Toolkit?

Redux is the core library—store, reducers, middleware. Redux Toolkit wraps it with createSlice, configureStore, Immer, default middleware, and RTK Query. You write less boilerplate and follow opinionated defaults that prevent common misconfiguration.

Can Redux Toolkit work with Laravel or Symfony backends?

Absolutely. The store is front-end only. It consumes REST or GraphQL endpoints from Laravel 13, Symfony 8.1, or any JSON API. Use cookies or bearer tokens in prepareHeaders, and keep authorization enforcement on the server.

Does Redux Toolkit replace React Context?

Not entirely. Context still fits theme, locale, and simple providers that rarely change. Redux Toolkit wins when many components subscribe to evolving data, you need middleware for async work, or DevTools time-travel debugging saves hours during incident response.

Ship Predictable Client State on Your Next React Build

State Management with Redux Toolkit earns its place when your React front end outgrows prop drilling and duplicated fetch code. Start with a typed store, feature slices, and RTK Query for list endpoints. Add thunks only where orchestration demands it. Keep business rules on the API. Test reducers early. Your future self—and the next developer on the project—will thank you on the first production bug.

Need a React dashboard wired to a Laravel API, payment gateway, or multi-role admin portal? Review the portfolio for shipped work, explore web development services, or contact us to discuss architecture before you commit to the wrong state layer.

Frequently Asked Questions

It means building a typed Redux store with configureStore, feature slices via createSlice, and optional RTK Query for server cache—replacing hand-written action types while keeping predictable, debuggable global state.

Yes, for medium and large React apps sharing complex client state or needing RTK Query's cached data layer. Small apps and mostly static sites should stay with local state or Context. RTK remains the documented Redux standard and integrates cleanly with TypeScript and Vite 8.x toolchains.

Skip it for mostly static pages, thin server-rendered HTML wrappers, or WordPress brochure sites that rarely need a client store. If React Context plus useReducer covers two or three shared values with no complex async flows, global Redux adds ceremony without payoff.

Scaffold with Vite 8.x and the react-ts template on Node.js 26 LTS, then install @reduxjs/toolkit and react-redux. Centralize configureStore in src/app/store.ts with feature reducers, wrap the root in Provider, and export typed useAppDispatch and useAppSelector hooks instead of raw useDispatch and useSelector everywhere.

Redux is the core library providing the store, subscriptions, and middleware pipeline. Redux Toolkit wraps it with createSlice, configureStore, Immer-powered reducers, default middleware, and RTK Query. You write less boilerplate and follow opinionated defaults that prevent common misconfiguration mistakes.

createSlice generates action creators and a reducer from a name, initial state, and reducer functions. Immer runs under the hood, so you write mutating-looking code that stays immutable. Export actions like addItem and clearCart, then register the default reducer export in configureStore alongside other feature slices.

Use createAsyncThunk for custom orchestration—retry logic, multi-step flows, or writing to several slices after one response. Handle pending, fulfilled, and rejected lifecycle states in extraReducers colocated with the slice. RTK Query fits stable CRUD list endpoints where cache bookkeeping would otherwise consume weeks of manual work.

Yes, when your UI is dominated by CRUD against REST or GraphQL. RTK Query generates hooks, caches responses, deduplicates in-flight requests, and refetches on focus or interval. Register its reducer and middleware in configureStore. The official Redux docs recommend RTK Query for server state and slices for client-only state.

Organize by feature, not technical type. Put src/app/ for store, root hooks, and global types. Each src/features/name/ folder holds its slice, selectors, API endpoints, and related UI. Keep src/shared/ for dumb components with no store imports. Colocate memoized createSelector exports in the feature folder to prevent unnecessary re-renders.

Absolutely. The store is front-end only and consumes REST or GraphQL from Laravel 13, Symfony 8.1, or any JSON API. Attach cookies or bearer tokens in RTK Query prepareHeaders, but keep authorization enforcement on the server. Treat the store as a thin client cache while the API remains authoritative for business rules.

Not entirely. Context still fits theme, locale, and simple auth flags that rarely change. Redux Toolkit wins when many distant components subscribe to evolving data, you need middleware for async work, or Redux DevTools time-travel debugging saves hours during incident response on larger admin dashboards.

Test reducers as pure functions by dispatching actions and asserting the next state—no DOM required. Mock fetch for thunk tests and assert pending and fulfilled action sequences. For RTK Query, use the library test utilities or MSW to stub HTTP at the network boundary. Pair unit tests with integration checks before release.

Non-serializable values like Dates, class instances, and DOM nodes trigger development warnings—convert at the boundary or keep them outside the store. Storing entire API responses without normalization causes stale duplicate entities across list views. Over-using global state for every modal flag bloats the store. Code-split routes with React.lazy to control bundle size on mobile networks.

JWT refresh tokens in memory are acceptable for SPAs, but never persist tokens to localStorage without threat modelling XSS. Read the token from auth slice state inside RTK Query prepareHeaders to set Authorization headers. Pair front-end auth storage with a hardened API layer using Sanctum or Passport on Laravel, regardless of what the UI optimistically displays.

Keep server truth in RTK Query—order lists, inventory panels, and fulfilment boards where caching, tag invalidation, and deduplication matter. Use slices for client-only state: auth session tokens, shopping cart items, UI theme, cross-page filters, and optimistic UI flags. Merging both into one blob makes the store unreadable six months later.

Share this article

0 Comments

Leave a comment

Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

Quick Contact Options
Choose how you want to connect me: