Simplify State Management with ZUSTAND

Let's break down how to use Zustand for client-side state management in a Next.js 15 app using the App Router and implement all CRUD functionalities (Create, Read, Update, Delete) for an eCommerce example.

Steps:

  1. Set up Zustand in a Next.js 15 app.

  2. Implement state management for cart functionality (add, update, remove items).

  3. Use Zustand to manage the state of the cart, including storing products and quantities.

  4. Connect it with the UI and explain each part of the code.


1. Install Zustand

First, make sure you have Zustand installed in your Next.js app:

npm install zustand

2. Create the Zustand Store

In the app directory, create a store for managing the cart state.

Create a file store/cartStore.ts (TypeScript):

import create from 'zustand';

// Cart state interface
interface CartItem {
  id: number;
  name: string;
  price: number;
  quantity: number;
}

// Zustand store definition
interface CartStore {
  cart: CartItem[];
  addItem: (item: CartItem) => void;
  removeItem: (itemId: number) => void;
  updateQuantity: (itemId: number, quantity: number) => void;
  clearCart: () => void;
}

export const useCartStore = create<CartStore>((set) => ({
  cart: [],

  // Add item to the cart
  addItem: (item) => set((state) => {
    const existingItem = state.cart.find((i) => i.id === item.id);
    if (existingItem) {
      // If item exists, update quantity
      return {
        cart: state.cart.map((i) =>
          i.id === item.id ? { ...i, quantity: i.quantity + 1 } : i
        ),
      };
    } else {
      // If item doesn't exist, add new item
      return { cart: [...state.cart, { ...item, quantity: 1 }] };
    }
  }),

  // Remove item from the cart
  removeItem: (itemId) => set((state) => ({
    cart: state.cart.filter((item) => item.id !== itemId),
  })),

  // Update item quantity
  updateQuantity: (itemId, quantity) => set((state) => ({
    cart: state.cart.map((item) =>
      item.id === itemId ? { ...item, quantity } : item
    ),
  })),

  // Clear the cart
  clearCart: () => set({ cart: [] }),
}));

Explanation:

  • useCartStore: This is our Zustand store. We define cart as an array to hold our cart items, and we define functions (addItem, removeItem, updateQuantity, clearCart) to manage the cart state.

  • addItem: Adds a new item to the cart or updates the quantity if the item already exists.

  • removeItem: Removes an item by its id.

  • updateQuantity: Updates the quantity of a specific item.

  • clearCart: Clears the entire cart.


3. Use the Cart Store in the UI

Now, let's create a simple UI where users can add items to the cart, update quantities, and remove items.

Example: app/page.tsx

import { useCartStore } from '../store/cartStore';

const ProductList = () => {
  const { addItem } = useCartStore();
   /*
    (we can use like this as well)
    const addItem = useCartStore((state) => state.addItem);
  */

  const products = [    { id: 1, name: 'Product 1', price: 100 },    { id: 2, name: 'Product 2', price: 150 },    { id: 3, name: 'Product 3', price: 200 },  ];

  return (
    <div>
      <h2>Products</h2>
      <ul>
        {products.map((product) => (
          <li key={product.id}>
            <h3>{product.name}</h3>
            <p>Price: ${product.price}</p>
            <button onClick={() => addItem(product)}>Add to Cart</button>
          </li>
        ))}
      </ul>
    </div>
  );
};

// This can be a completely new component

const Cart = () => {
  const { cart, removeItem, updateQuantity, clearCart } = useCartStore();
   /*
    (we can use like this as well)
    const cart = useCartStore((state) => state.cart);
    const removeItem = useCartStore((state) => state.removeItem);
    const updateQuantity = useCartStore((state) => state.updateQuantity);
    const clearCart = useCartStore((state) => state.clearCart);
  */

  if (cart.length === 0) {
    return <p>Your cart is empty!</p>;
  }

  return (
    <div>
      <h2>Your Cart</h2>
      <ul>
        {cart.map((item) => (
          <li key={item.id}>
            <h3>{item.name}</h3>
            <p>Price: ${item.price}</p>
            <p>Quantity: {item.quantity}</p>
            <button onClick={() => updateQuantity(item.id, item.quantity + 1)}>Increase Quantity</button>
            <button onClick={() => updateQuantity(item.id, item.quantity - 1)}>Decrease Quantity</button>
            <button onClick={() => removeItem(item.id)}>Remove</button>
          </li>
        ))}
      </ul>
      <button onClick={clearCart}>Clear Cart</button>
      <p>
        Total: ${cart.reduce((total, item) => total + item.price * item.quantity, 0)}
      </p>
    </div>
  );
};

const Page = () => (
  <div>
    <ProductList />
    <Cart />
  </div>
);

export default Page;

Explanation:

  • ProductList: Displays a list of products, and each product has an "Add to Cart" button that calls addItem from the Zustand store to add the product to the cart.

  • Cart: Displays the contents of the cart with each item's quantity. You can increase or decrease the quantity, or remove items using the functions updateQuantity and removeItem. A total is displayed by summing up the prices of all items in the cart.


4. Explanation of CRUD Functionalities

  1. Create: The addItem function adds a product to the cart. If the item is already in the cart, it increases the quantity by 1.

  2. Read: The cart items are displayed in the UI. The current state of the cart is managed and accessed via the useCartStore hook.

  3. Update: The updateQuantity function updates the quantity of an item. The UI provides buttons to increase or decrease the quantity.

  4. Delete: The removeItem function removes an item from the cart, and clearCart removes all items.


5. Running the App

After implementing this, you should be able to:

  • View a list of products.

  • Add products to the cart.

  • Increase or decrease quantities of items in the cart.

  • Remove items from the cart or clear the entire cart.

You can run the app by starting the Next.js development server:

npm run dev

This setup provides a clean and efficient way to manage client-side state using Zustand in a Next.js 15 app with the App Router, and covers all CRUD operations for an eCommerce cart functionality.