Next js

13. Mastering Redux Toolkit in Next.js 14 with TypeScript

Enterprise-Scale State Management for Real-World Applications

While Zustand is great for lightweight global state,
Redux Toolkit shines in
enterprise applications where:

  • You have deeply nested or shared state
  • You want middleware (e.g. logging, async thunks)
  • You need central debugging with Redux DevTools
  • Your app structure is growing across teams/modules

In this guide, you’ll learn how to fully integrate
Redux Toolkit in a
Next.js 14 App Router + TypeScript project — step-by-step.


✅ When to Use Redux Toolkit (RTK)

Use Case Recommendation
Small state (theme, cart) Zustand
Global complex shared state Redux Toolkit
Async data fetching + caching Redux Toolkit Query
Modular, scalable architecture Redux Toolkit

✅ Folder Structure for Redux

src/
├── store/
│ ├── redux/
│ │ ├── store.ts
│ │ ├── counterSlice.ts
│ │ └── productSlice.ts (optional)
├── app/
│ └── layout.tsx (wrap Provider here)
├── components/
│ └── ReduxCounter.tsx


🧪 Step 1: Install Redux Toolkit

npm install @reduxjs/toolkit react-redux

🔹 store.ts (Configure Redux Store)

// src/store/redux/store.ts
import { configureStore } from "@reduxjs/toolkit";
import counterReducer from "./counterSlice";

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

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


🔹 counterSlice.ts (Create Feature Slice)

// src/store/redux/counterSlice.ts
import { createSlice } from "@reduxjs/toolkit";

const initialState = { value: 0 };

const counterSlice = createSlice({
  name: "counter",
  initialState,
  reducers: {
    increment(state) {
      state.value += 1;
    },
    decrement(state) {
      state.value -= 1;
    },
    reset(state) {
      state.value = 0;
    },
  },
});

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

 


🔹 Provide Store in Layout

// src/app/layout.tsx
import { store } from "@/store/redux/store";
import { Provider } from "react-redux";

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html>
      <body>
        <Provider store={store}>
          {children}
        </Provider>
      </body>
    </html>
  );
}

🧪 Redux Usage Example

// src/components/ReduxCounter.tsx
"use client";

import { useSelector, useDispatch } from "react-redux";
import { RootState } from "@/store/redux/store";
import { increment, decrement, reset } from "@/store/redux/counterSlice";

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

  return (
    <div className="space-y-2 p-4 border max-w-md">
      <h2 className="text-xl font-bold">Redux Counter</h2>
      <p>Count: {count}</p>
      <div className="flex gap-2">
        <button
          onClick={() => dispatch(increment())}
          className="bg-blue-500 px-4 py-1 text-white rounded"
        >
          +1
        </button>
        <button
          onClick={() => dispatch(decrement())}
          className="bg-red-500 px-4 py-1 text-white rounded"
        >
          -1
        </button>
        <button
          onClick={() => dispatch(reset())}
          className="bg-gray-500 px-4 py-1 text-white rounded"
        >
          Reset
        </button>
      </div>
    </div>
  );
}

🧠 Bonus: Async Logic with createAsyncThunk

🔹 productSlice.ts Example

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

// Async thunk to fetch products
export const fetchProducts = createAsyncThunk("product/fetch", async () => {
  const res = await fetch("/api/products");
  return res.json();
});

// Product slice
const productSlice = createSlice({
  name: "product",
  initialState: {
    items: [],
    loading: false,
  },
  reducers: {},
  extraReducers: (builder) => {
    builder
      .addCase(fetchProducts.pending, (state) => {
        state.loading = true;
      })
      .addCase(fetchProducts.fulfilled, (state, action) => {
        state.items = action.payload;
        state.loading = false;
      });
  },
});

export default productSlice.reducer;

✅ Summary

Task Implementation
Setup Redux store configureStore()
Create global feature slice createSlice()
Dispatch actions in UI useDispatch(), useSelector()
Async calls (optional) createAsyncThunk()
App-wide state management Ideal for large apps or teams

Leave a Reply

Your email address will not be published. Required fields are marked *