<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Sanjokale</title>
    <description>The latest articles on DEV Community by Sanjokale (@sanjokale).</description>
    <link>https://dev.to/sanjokale</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1245194%2F2ba64162-b9c6-47a2-8476-15d76015a754.jpeg</url>
      <title>DEV Community: Sanjokale</title>
      <link>https://dev.to/sanjokale</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sanjokale"/>
    <language>en</language>
    <item>
      <title>JWT Authentication in Express.js using Cookies</title>
      <dc:creator>Sanjokale</dc:creator>
      <pubDate>Sat, 08 Mar 2025 07:30:36 +0000</pubDate>
      <link>https://dev.to/sanjokale/jwt-authentication-in-expressjs-using-cookies-27gi</link>
      <guid>https://dev.to/sanjokale/jwt-authentication-in-expressjs-using-cookies-27gi</guid>
      <description>&lt;p&gt;JSON Web Tokens (JWTs) are a crutial part of modern web application security. A JWT is an open standard(RFC 7519) that defines a compact and self-contained way transmitting information between parties as a JSON object.Essentially, it's a string of characters that carries information.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Structure:&lt;/strong&gt; It consist of three parts, seperated by dot(.):-&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Header: Contains metadata about the token, such as the signing algorithm.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Payload: Contains the "claims," which are statements about an entity (e.g., a user) and additional data.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Signature: Used to verify that the token hasn't been tampered with.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F73chiasjj4ktltaz8u8b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F73chiasjj4ktltaz8u8b.png" alt="Image description" width="800" height="627"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Uses&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Authenticaiton: When a user logs in, the server can generate a JWT and send it to the client.The client then includes this JWT in subsequent requests, allowing the server to verify the user's identity without needing to store session information on the server itself (stateless authentication).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Authorization: JWTs can contain information about a user's permissions, allowing the server to determine whether the user is authorized to access certain resources.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Information Exchange: Because JWTs can be signed, they provide a secure way to transmit information between parties, ensuring that the information hasn't been modified.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Statelessness: One of the key benefits of JWTs is that they enable stateless authentication. This means that the server doesn't need to maintain session data, which can improve scalability.  &lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Implementing JWT authenticaiton in an Express.js application involves several key steps:-
&lt;/h2&gt;

&lt;p&gt;However, there are other ways to send the jwt to the forntend and today i will teach you how to store the jwt in a cookie.&lt;br&gt;
Here I use cookies because sometimes I'm don't feel like constantly sending the jwt in the headers whenever i make a request to the Api. This is where cookies come in, you can send them whenever you make an http request without worry.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Install Dependencies:&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;npm i bcrypt cookie-parser jsonwebtoken dotenv&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;configure cookieParser in index.js&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//index.js
import express from "express";
import cors from "cors";
import cookieParser from "cookie-parser";
import dotenv from "dotenv";
import connectDB from "./db/index.js";

dotenv.config({
  path: "./.env",
});

const app = express();

const port = 8080

//app.use() is use to set the configurations and the middlewares
//middleware helps to changes to the request and the response objects
//middleware can end the request-response cycle
//middleware call the next middleware function in the stack

app.use(
  cors({
    origin: process.env.CORS_ORIGIN,
    credentials: true,
  })
);

connection();  //connection database to the server

app.use(express.json({ limit: "16kb" }));
app.use(express.urlencoded({ extended: true, limit: "16kb" })); //extended helps us to pass the nested object
app.use(cookieParser()); cookie configuration in the app

//routes imports
import userRouter from "./routes/user.routes.js";

//router declaration
app.use("/api/v1/users", userRouter);

app.listen(port, ()=&amp;gt;{
    console.log("server is started in port" + port)
})

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Create a .env file in your root directory and configure your enviroment variables&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//.env

PORT = 8080
CORS_ORIGIN = *
ACCESS_TOKEN_SECRET = accesstokensecret
ACCESS_TOKEN_EXPIRY = 10d
REFRESH_TOKEN_SECRET = refreshtokensecret
REFRESH_TOKEN_EXPIRY = 10d
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Create a user.model.js file in models folder&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//user.model.js
import mongoose, { Schema } from "mongoose";
import bcrypt from "bcrypt";
import jwt from "jsonwebtoken";

const userSchema = new Schema(
  {
    email: {
      type: String,
      required: true,
      unique: true,
      lowercase: true,
      trim: true,
    },
    fullname: {
      type: String,
      required: true,
      trim: true,
      index: true,
    },

   password: {
      type: String,
      required: [true, "password is required"],
    },
    refreshToken: {
      type: String,
    },
  },
);

userSchema.pre("save", async function (next) {
  //here we use normal function instead of arrow function because of arrow function doesnot hold the current context in "this" keyword. here pre is the mogoose hook which is execute before the save method. this is like a middleware which is execute whenever password is changes before save in the database.
  if (!this.isModified("password")) return next();
  this.password = await bcrypt.hash(this.password, 10);  //this method is execute while the password is changes before save in the database
  next();
});

//in mongoose we can inject the custom methods to it just like that middleware
// The userSchema in your Mongoose setup plays a crucial role in defining instance methods, such as isPasswordCorrect.

userSchema.methods.isPasswordCorrect = async function (password) {
  return await bcrypt.compare(password, this.password);
  //In the context of instance methods, this refers to the specific instance of the user document. This is crucial because it allows the method to access instance-specific properties, such as this.password, which contains the hashed password for that particular user.
};

userSchema.methods.generateAccessToken = function () {  //here in the context of instance method this refers to the specific instance of the user document and we can access the instance specific properties. and here we addded the method to the userSchema so that we can access this method to the user instance.
  return jwt.sign(
    {
      _id: this._id,
      email: this.email,
      username: this.username,
      fullname: this.fullname,
    },
    process.env.ACCESS_TOKEN_SECRET,
    {
      expiresIn: process.env.ACCESS_TOKEN_EXPIRY,
    }
  );
};

userSchema.methods.generateRefreshToken = function () {
  return jwt.sign(
    {
      _id: this._id,
    },
    process.env.REFRESH_TOKEN_SECRET,  
    {
      expiresIn: process.env.REFRESH_TOKEN_EXPIRY,
    }
  );
};

export const User = mongoose.model("User", userSchema);

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Create a user.controller file in controller folder&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//user.controller.js
import { User } from "../models/user.model.js";
import jwt from "jsonwebtoken";

const generateAccessAndRefreshTokens = async (userId) =&amp;gt; {
  try {
    const user = await User.findById(userId);
    const accessToken = user.generateAccessToken();
    const refreshToken = user.generateAccessToken();

    user.refreshToken = refreshToken;
    await user.save({ validateBeforeSave: false }); //save the refresh token to the user document.
    //The save method in Mongoose is used to persist changes made to a document in the MongoDB database.

    return { accessToken, refreshToken };
  } catch (error) {
    throw new Error(
      "Something Went Wrong while generationg refresh and access token"
    );
  }
};

const registerUser = async (req, res) =&amp;gt; {

const { fullname, email, password } = req.body;

  if (
    [fullname, email, password].some((field) =&amp;gt; field?.trim() === "")
  ) {
    //The some() method is an iterative method, which means it calls a provided callbackFn function once for each element in an array, until the callbackFn returns a truthy value. If such an element is found, some() immediately returns true and stops iterating through the array. Otherwise, if callbackFn returns a falsy value for all elements, some() returns false.

    throw new Error("All fields are required");
  }

  const existedUser = await User.findOne({ email});

  if (existedUser) {
    throw new Error("Username or Email already exists");
  }

  const user = await User.create({
    fullname,
    email,
    password,
  });

  const createdUser = await User.findById(user._id).select(
    //the select method is not a native JavaScript method. It's a method provided by the Mongoose library, which is a popular ORM (Object Relational Mapping) tool for MongoDB in Node.js.
    "-password -refreshToken"
  );

  if (!createdUser) {
    throw new Error("something went wrong while registering the user");
  }

  return res
    .status(201)
    .send({ msg: "User resgisterd successfully"});
};




const loginUser = async (req, res) =&amp;gt; {

  const { email, password } = req.body;


  if (!email) {
    throw new Error("username or password is required");
  }

  //this is the database query
  const user = await User.findOne({ email });

  //here user is mine user that i make it is the instance of User

  if (!user) {
    throw new Error( "User does not exist");
  }

  const isPasswordValid = await user.isPasswordCorrect(password); //instance method to check password is valid or invalid

  if (!isPasswordValid) {
    throw new Error("invalid user credentials");
  }

  const { accessToken, refreshToken } = await generateAccessAndRefreshTokens(
    user._id
  );

  const loggedInUser = await User.findById(user._id).select(
    "-password -refreshToken"
  );

  //send cookies
  const options = {
    httpOnly: true,
    secure: true,
  }; //this help to make cookie modifiable only from server. Generally cookies can modified from both frontend and backend

  return res
    .status(200)
    .cookie("accessToken", accessToken, options) //this line of code help to save the tokens to cookie.
    .cookie("refreshToken", refreshToken, options)
    .send(
        {
          user: loggedInUser,
          accessToken,
          refreshToken,
        },
        )
   };





const logoutUser = async (req, res) =&amp;gt; {
  await User.findByIdAndUpdate(
    req.user._id,
    {
      $unset: {
        refreshToken: 1,
      },
    },
    {
      new: true,  //here new is for it return the data after updated.
    }
  );

  const options = {
    httpOnly: true,
    secure: true,
  };
  return res
    .status(200)
    .clearCookie("accessToken", options)  //this line of code helps to remove the cookie data 
    .clearCookie("refreshToken", options)
    .send( {msg:  "User logged Out Successfully"});
};

export {registerUser, loginUser, logoutUser}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;create create as auth.middleware file in the middlewares folder. This middleware will check if the request has a valid JWT in its header.&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//auth.middleware.js
import { User } from "../models/user.model.js";
import { asyncHandler } from "../utils/asyncHandler.js";
import jwt from "jsonwebtoken";

export const verifyJWT = async (req, _, next) =&amp;gt; {
  try {
    const token =
      req.cookies?.accessToken ||
      req.header("Authorization")?.replace("Bearer ", ""); //replace method delete the substring ofstring

    if (!token) {
      throw ApiError(401, "Unauthorized request");
    }

    const decodedToken = jwt.verify(token, process.env.ACCESS_TOKEN_SECRET);

    const user = await User.findById(decodedToken?._id).select(
      "-password -refreshToken"
    );

    if (!user) {
      //next video discuss about frontend
      throw new Error( "Invalid Access Token");
    }

    req.user = user;  //giving user information to the req object.****
    next();
  } catch (error) {
    throw new Error( "Invalid Access Token");
  }
};

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Create a user.route.js file inside the routes folder&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//user.route.js
import { Router } from "express";
import {
  loginUser,
  logoutUser,
  registerUser,
} from "../controllers/user.controller.js";
import { verifyJWT } from "../middlewares/auth.middleware.js";


const router = Router();

router.route("/register").post(registerUser);

router.route("/login").post(loginUser);

//secured routes --&amp;gt; protected router 
//The protected route uses verifyToken to ensure that only authenticated users can access it.
router.route("/logout").post(verifyJWT, logoutUser); // user can only logout if user is verifyjwt authenticated.

export default router;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;`make sure you have imort cookieParser from the "cookie-parser" and set configuration or middleware like &lt;strong&gt;app.use(cookieParser)&lt;/strong&gt; in the index.js file.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This detailed explaination and code blocks give you a very good starting point for implementing JWT authentication in your Express applications.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
    </item>
    <item>
      <title>Minimal way to setup Redux toolkit</title>
      <dc:creator>Sanjokale</dc:creator>
      <pubDate>Fri, 07 Mar 2025 12:25:23 +0000</pubDate>
      <link>https://dev.to/sanjokale/minimal-way-to-setup-redux-toolkit-4pg</link>
      <guid>https://dev.to/sanjokale/minimal-way-to-setup-redux-toolkit-4pg</guid>
      <description>&lt;p&gt;Redux is a predictable state container for javascript applications. It is popular in react applications, but it can be used with other frameworks as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Reasons For Using Redux:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Centralized State Management:&lt;/strong&gt;Redux provides a single, centralized store to manage the applicaiton's state. This makes it easier to track and manage state changes, especially in complex applcations with many components.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Facilitated Complex State Logic:&lt;/strong&gt;Redux provides a structured way to manage it, preventing issues like "prop drilling"&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;State Persistence:&lt;/strong&gt;Libraries like Redux persist, allow for saving application states, through page refreshes, or even applicaiton restarts.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Follow these steps to integrate Redux Toolkit into your NextJs appication:-
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Installation&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;npm install @reduxjs/toolkit react-redux&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Configuring Redux Toolkit in Next.js (app router)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Create a redux folder to store the files related to redux setup files in /src directory. Inside the src/redux directory create a &lt;code&gt;store.js&lt;/code&gt; file.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Configure the Redux store in &lt;code&gt;store.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// store.js
import { configureStore } from '@reduxjs/toolkit';
import userReducer from './userSlice'; // Import your userSlice

const store = configureStore({
  reducer: {
    user: userReducer, // Add your user reducer to the store
    // ... other reducers if you have them
  },
});

export default store;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Create folder in your src/redux directory and after that create a slices. Redux slices manage a section of the state and contain actions and raducers.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//redux/slices/userSlice.js

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

const initialState = {
  userDetails: null, // Initially, no user details
};

const userSlice = createSlice({
  name: 'user',
  initialState,
  reducers: {
    setUserDetails: (state, action) =&amp;gt; {
      state.userDetails = action.payload;
    },
    clearUserDetails: (state) =&amp;gt; {
      state.userDetails = null;
    },
  },
});

export const { setUserDetails, clearUserDetails } = userSlice.actions;
export default userSlice.reducer;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;To make Redux available across your Next.js app, wrap the Redux Provider.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//RootLayout layout.js
'use client'; // Required for client-side hooks

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

export default function RootLayout({ children }) {
  return (
    &amp;lt;html lang="en"&amp;gt;
      &amp;lt;body&amp;gt;
        &amp;lt;Provider store={store}&amp;gt;
          {children}
        &amp;lt;/Provider&amp;gt;
      &amp;lt;/body&amp;gt;
    &amp;lt;/html&amp;gt;
  );
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Now that redux is setup, let's connect it to a component using useSelector and useDispatch hooks.&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;useSelector&lt;/code&gt;: This hook allow you to extract data from redux store or selects the Redux state.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;useDispatch&lt;/code&gt;: This hook returns a reference to the dispatch funciton from the Redux store. You use the dispatch function to send the action to store which trigger state update.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//Example componenet (e.g., userProfile.js)
'use client'
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { setUserDetails, clearUserDetails } from './userSlice';

function UserProfile() {
  const userDetails = useSelector((state) =&amp;gt; state.user.userDetails);
  const dispatch = useDispatch();

//here data is come from the api call of getUser
  const mockUserDetails = {
    userId: 'user123',
    username: 'john.doe',
    firstName: 'John',
    lastName: 'Doe',
    email: 'john.doe@example.com',
    phoneNumber: '123-456-7890',
    address: {
      street: '123 Main St',
      city: 'Anytown',
      state: 'CA',
      zip: '91234',
      country: 'USA',
    },
    dateOfBirth: '1990-05-15',
    registrationDate: '2023-10-26T10:00:00Z',
    lastLogin: '2023-10-27T14:30:00Z',
    isActive: true,
    profilePicture: 'https://example.com/images/user123.jpg',
  };

  const handleSetUserDetails = () =&amp;gt; {
    dispatch(setUserDetails(mockUserDetails));
  };

  const handleClearUserDetails = () =&amp;gt; {
    dispatch(clearUserDetails());
  };

  if (!userDetails) {
    return (
      &amp;lt;div&amp;gt;
        &amp;lt;p&amp;gt;No user details available.&amp;lt;/p&amp;gt;
        &amp;lt;button onClick={handleSetUserDetails}&amp;gt;Set User Details&amp;lt;/button&amp;gt;
      &amp;lt;/div&amp;gt;
    );
  }

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;h2&amp;gt;User Profile&amp;lt;/h2&amp;gt;
      &amp;lt;p&amp;gt;Username: {userDetails.username}&amp;lt;/p&amp;gt;
      &amp;lt;p&amp;gt;Email: {userDetails.email}&amp;lt;/p&amp;gt;
      {/* ... other user details */}
      &amp;lt;button onClick={handleClearUserDetails}&amp;gt;Clear User Details&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

export default UserProfile;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Implementing redux middleware like redux logger and redux persist
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;redux-logger&lt;/strong&gt;: Logs all Redux actions and state changes to the console, making it easy to debug your Redux store&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;redux-persist&lt;/strong&gt;: Saves the Redux store's state to localStorage (or another storage engine). When the app is reloaded, redux-perdidt loads the saved state back into the store&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;PersistGate&lt;/strong&gt;: Ensure that the app doesn't render until the persisted state is loaded, preventing potential issues with rendeing componets that rely on the persisted data.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Installation&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;npm install redux-logger redux-persist&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;key changes on these files I mentioned above&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//store.js
'use client'
import { combineReducers, configureStore } from "@reduxjs/toolkit"
import userReducer from "../slices/userSlice"
import logger from "redux-logger"
import { persistStore, persistReducer } from "redux-persist";
import storage from "redux-persist/lib/storage";
const persistConfig = {
  key: "root",  //key for local storage
  storage,
};

const rootReducer = combineReducers({ 
  user: userReducer,
})
const persistedReducer = persistReducer(persistConfig,rootReducer);


export const store = configureStore({
  reducer: persistedReducer,
  middleware: ()=&amp;gt; [logger]
})

export const persistor = persistStore(store);

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//layout.js
'use client'; // Required for client-side hooks
import { persistor, store } from "@/redux/store"
import { PersistGate } from "redux-persist/integration/react"


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

export default function RootLayout({ children }) {
  return (
    &amp;lt;html lang="en"&amp;gt;
      &amp;lt;body&amp;gt;
        &amp;lt;Provider store={store}&amp;gt;
          &amp;lt;PersistGate loading={null} 
           persistor={persistor}&amp;gt;
          {children}
          &amp;lt;/PersistGate&amp;gt;
        &amp;lt;/Provider&amp;gt;
      &amp;lt;/body&amp;gt;
    &amp;lt;/html&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This setup will persist the user details across browser reloads, and you will able to see the redux action and state changes in the console.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo27ysvgesqvw48zdi51m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo27ysvgesqvw48zdi51m.png" alt="Image description" width="759" height="316"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>redux</category>
      <category>react</category>
      <category>reduxtool</category>
    </item>
  </channel>
</rss>
