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:
Set up Zustand in a Next.js 15 app.
Implement state management for cart functionality (add, update, remove items).
Use Zustand to manage the state of the cart, including storing products and quantities.
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 definecart
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 itsid
.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
andremoveItem
. A total is displayed by summing up the prices of all items in the cart.
4. Explanation of CRUD Functionalities
Create: The
addItem
function adds a product to the cart. If the item is already in the cart, it increases the quantity by 1.Read: The cart items are displayed in the UI. The current state of the cart is managed and accessed via the
useCartStore
hook.Update: The
updateQuantity
function updates the quantity of an item. The UI provides buttons to increase or decrease the quantity.Delete: The
removeItem
function removes an item from the cart, andclearCart
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.