Skip to main content
imvinojanv.dev
  • About
  • Blog
  • Projects
  • Snippets

Command Palette

Search for a command to run...

imvinojanv.dev
Open to Hire

I'm always open to discussing software engineering work or partnership.

HomeAboutResumeUses
BlogProjectsSnippets
© 2026 Vinojan Veerapathirathasan —— Colombo, Sri Lanka.
MediumGitHubLinkedInRSS
Back

Effortless State Management in Next.js with Zustand.

Simplify Your React Applications by Mastering Zustand in Next.js for Seamless Global State Handling.

Published on July 16, 2024
7 min read
Effortless State Management in Next.js with Zustand.

Hi there 👋,

Welcome back to another enlightening journey into the modern web development landscape. Today, we're exploring a powerful pairing in the React ecosystem — Zustand with Next.js. 🚀

📑 In this article, we'll unravel how Zustand simplifies state management in Next.js applications, making it easier than ever to maintain and scale your global state. Are you ready to streamline your codebase and supercharge your web projects? Let's jump right in! 🎉

🐻 Zustand is a minimalist state management library designed for React applications, renowned for its simplicity and straightforward setup. Unlike more complex state management solutions, Zustand offers a lean API that makes it incredibly easy to use, while still being powerful enough to handle global state across scalable applications.

What Sets Zustand Apart?

One of the key features of Zustand is its unopinionated approach. It doesn't prescribe a specific architecture or demand significant boilerplate, making it an excellent choice for developers looking for a flexible, low-overhead solution. Zustand operates on a simple premise: you define a store, and it provides a set of hooks that allow components to interact seamlessly with this store.

Benefits of Using Zustand 🐻

  • Simplicity: Zustand's API is straightforward. There are no reducers, middlewares, or complex configurations to wrestle with.
  • Performance: Zustand uses a subscription-based model, which is highly optimized for performance. Components only re-render when the state slices they subscribe to are updated, minimizing unnecessary renders.
  • Flexibility: It can be integrated into any component, regardless of its position in the component tree, without the need for context providers or higher-order components.
  • TypeScript Support: For projects using TypeScript, Zustand provides excellent type support, ensuring that state management is both robust and error-free.

Build a Simple App with Zustand

#1. Setting up the Next.js project

First, set up a new Next.js project if you don't have one already:

npx create-next-app@latest zustand-app
Would you like to use TypeScript? Yes
Would you like to use ESLint? Yes
Would you like to use Tailwind CSS? Yes
Would you like to use src/ directory? No
Would you like to use App Router? (recommended) Yes
Would you like to customize the default import alias (@/)? No

Open the project with your code editor (vs code).

#2: Installing Zustand

Install Zustand:

npm install zustand

#3: Creating the Store

Create a new file store/useCounterStore.ts to set up the Zustand store:

/store/useCounterStore.ts
import create from "zustand";
 
type CounterState = {
  count: number; // This holds the current count
  increase: () => void; // Action to increase the count
  decrease: () => void; // Action to decrease the count
};
 
export const useCounterStore = create<CounterState>((set) => ({
  count: 0, // Initial state of the count
  increase: () => set((state) => ({ count: state.count + 1 })), // Increments the count
  decrease: () => set((state) => ({ count: state.count - 1 })), // Decrements the count
}));

In the above code, create is a factory function that receives a setup function. This setup function uses set, which allows us to modify the state. The increase and decrease functions use this set method to update the state based on current state values.

#4: Building the Counter Component

Create a new component components/counter.tsx. The Counter component interacts with our Zustand store and provides a user interface to display and modify the count.

/components/counter.tsx
import { useCounterStore } from '@/store/useCounterStore';
 
const Counter: React.FC = () => {
    const { count, increase, decrease } = useCounterStore();
 
    return (
        <div className="p-4 flex flex-col items-center justify-center">
            <div className="text-2xl font-bold">{count}</div>
            <div className="flex space-x-4 mt-4">
                <button
                    className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-700"
                    onClick={increase}
                >
                    Increase
                </button>
                <button
                    className="px-4 py-2 bg-red-500 text-white rounded hover:bg-red-700"
                    onClick={decrease}
                >
                    Decrease
                </button>
            </div>
        </div>
    );
};
 
export default Counter;

In this component, useCounterStore hooks into the Zustand store, allowing us to access and modify the count state. The component renders the current count and provides buttons to increase and decrease the count.

#5: Integrating the Component

Update app/page.tsx to include the Counter component:

/app/page.tsx
import Counter from '@/components/counter';
 
const Home = () => {
    return (
        <div className="flex justify-center items-center min-h-screen">
            <Counter />
        </div>
    );
};
 
export default Home;

This page serves as the entry point for your application. Here, the Counter component is rendered inside a flex container that centers it on the page.

#6: Running Application

npm run dev

Navigate to http://localhost:3000 to see your counter application in action. You should be able to increase and decrease the count using the buttons.

This setup demonstrates a practical use of Zustand for state management in a Next.js project, offering a simple yet powerful solution for managing a global state.

Utilize the Zustand's persist with the same example

#1: Setting Up Zustand Stores

Create a file called useAuthStore.ts in your store directory. This store will handle user authentication state.

/store/useAuthStore.ts
import create from "zustand";
import { persist } from "zustand/middleware";
 
interface AuthState {
  user: { name: string; email: string } | null;
  login: (name: string, email: string) => void;
  logout: () => void;
}
 
export const useAuthStore = create<AuthState>(
  persist(
    (set) => ({
      user: null, // Initial state: no user is logged in
      login: (name, email) => set({ user: { name, email } }),
      logout: () => set({ user: null }),
    }),
    {
      name: "auth-storage", // Name of the storage item
      getStorage: () => sessionStorage, // Define the type of storage
    },
  ),
);

In this store, login and logout actions update the user state. We use the persist middleware from Zustand, which enables the store to save its state to sessionStorage. This means the user's login state is preserved across page refreshes, but not across browser sessions.

#2: Integrating AuthStore with Counter Component

/components/counter.tsx
import { useCounterStore } from '@/store/useCounterStore';
import { useAuthStore } from '@/store/useAuthStore';
 
const Counter: React.FC = () => {
    const { count, increase, decrease } = useCounterStore();
    const { user, login, logout } = useAuthStore();
 
    return (
        <div>
            {!user ? (
                <div>
                    <p>Welcome, Please login!</p>
                    <button onClick={() => login('John Doe', 'john@example.com')}>Login</button>
                </div>
            ) : (
                <>
                <p>Welcome, {user.name}!</p>
                <button onClick={logout}>Logout</button>
 
                <div className="p-4 flex flex-col items-center justify-center">
                  <div className="text-2xl font-bold">{count}</div>
                  <div className="flex space-x-4 mt-4">
                      <button
                          className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-700"
                          onClick={increase}
                      >
                          Increase
                      </button>
                      <button
                          className="px-4 py-2 bg-red-500 text-white rounded hover:bg-red-700"
                          onClick={decrease}
                      >
                          Decrease
                      </button>
                  </div>
               </div>
               </>
            )}
        </div>
    );
};
 
export default Counter;

This component displays a welcome message and the counter function if the user is logged in or a login button if no user is logged in. Clicking the login button logs in a static user, while the logout button clears the user state.

Handling user authentication using Zustand's persist middleware allows for maintaining session state even after page reloads, ensuring a seamless user experience.

This scenario illustrates how Zustand can be leveraged to manage multiple state aspects in a Next.js application.

🍃 This guide offered a detailed exploration of integrating Zustand with Next.js, showcasing how to manage both authentication and theme settings seamlessly. With Zustand, you unlock the potential to handle complex state management scenarios with ease, ensuring your applications are both scalable and maintainable.


I appreciate you taking the time to read this article.🙌

Before you move on to explore the next article, don't forget to give your claps 👏 for this article and share with your friends. Stay connected with me on social media. Thanks for your support and have a great rest of your day! 🎊

✍️ Vinojan Veerapathirathasan.

0
0
0
0
0

On This Page

What Sets Zustand Apart?Benefits of Using Zustand 🐻Build a Simple App with Zustand#1. Setting up the Next.js project#2: Installing Zustand#3: Creating the Store#4: Building the Counter Component#5: Integrating the Component#6: Running ApplicationUtilize the Zustand's persist with the same example#1: Setting Up Zustand Stores#2: Integrating AuthStore with Counter Component
Last updated: July 16, 2024