I’ve been building with Next.js for years, and there’s one question that always comes up as an application grows: how do we manage state without making a mess? We’ve all been there—props drilled through five components, Context providers nested like Russian dolls, or the sheer boilerplate of heavier libraries. Recently, I found myself reaching for a simpler tool, and that’s how I started using Zustand. It felt like finding a clean, well-organized drawer for all the scattered pieces of my application’s logic. Let’s talk about why combining it with Next.js might just be the straightforward solution you’re looking for.
Think about the last time you built a dashboard or an e-commerce cart. You needed a piece of data—a user’s name, a list of selected items—to be available in multiple, far-apart components. In a standard React app, this can get complicated. In a Next.js app, which mixes server and client, it can feel even more complex. This is where Zustand steps in. It offers a way to create a shared data store without wrapping your entire application in providers. You create a store, and any component can tap into it. It’s that simple.
So, what does this look like in practice? Let’s create a basic store. Imagine we’re building a theme switcher for a Next.js site.
// stores/themeStore.js
import { create } from 'zustand';
const useThemeStore = create((set) => ({
theme: 'light',
toggleTheme: () => set((state) => ({
theme: state.theme === 'light' ? 'dark' : 'light'
})),
}));
export default useThemeStore;
Now, in any client component, you can use this hook directly.
// components/ThemeToggle.jsx
'use client';
import useThemeStore from '@/stores/themeStore';
export default function ThemeToggle() {
const { theme, toggleTheme } = useThemeStore();
return (
<button onClick={toggleTheme}>
Current theme: {theme}
</button>
);
}
Notice there’s no <Provider> in sight. The component just imports the store and uses it. This simplicity is liberating, especially in a Next.js project where you’re already managing server components, client components, and data fetching. But here’s a question: what happens when you need that initial theme value to come from the server, perhaps from a user’s saved preference?
This is where the Next.js and Zustand combination gets interesting. Next.js excels at fetching data on the server. You can fetch a user’s preferred theme in getServerSideProps or a Server Component and then pass it to the client. The trick is initializing your Zustand store with that server data to avoid a flash of incorrect content. One effective pattern is to pass the initial state as a prop and use it to create the store.
// app/page.jsx (or in getServerSideProps)
import DashboardClient from '@/components/DashboardClient';
export default async function HomePage() {
// Fetch data on the server
const serverData = await fetchUserPreferences();
return <DashboardClient serverTheme={serverData.theme} />;
}
Then, in your client component, you can initialize the store with that prop.
// components/DashboardClient.jsx
'use client';
import { useEffect } from 'react';
import useThemeStore from '@/stores/themeStore';
export default function DashboardClient({ serverTheme }) {
// Initialize the store with server data
useEffect(() => {
if (serverTheme) {
useThemeStore.setState({ theme: serverTheme });
}
}, [serverTheme]);
// ... rest of your component
}
This approach ensures a smooth transition from the server-rendered page to the interactive client state. The user sees their preferred theme immediately, and the interactive toggle works instantly. It bridges the server-client gap elegantly.
But state isn’t just about UI themes. Consider a shopping cart. You want the cart state to persist not just across component re-renders, but often across browser sessions. Zustand has a vibrant middleware ecosystem for this. The persist middleware can sync your store to localStorage with just a few lines.
// stores/cartStore.js
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
const useCartStore = create(
persist(
(set, get) => ({
items: [],
addItem: (product) => set((state) => ({
items: [...state.items, product]
})),
clearCart: () => set({ items: [] }),
}),
{
name: 'cart-storage', // unique name for localStorage key
}
)
);
Now, a user can add items to their cart, close the browser, and come back later to find their cart intact. This powerful feature is added with minimal code. It makes you wonder: how much complexity have we been adding with other tools that this library handles so simply?
Performance is a cornerstone of Next.js, and a state library should not weigh it down. Zustand is incredibly lightweight. Its small bundle size means you’re not undoing Next.js’s optimization efforts. Your pages stay fast. Furthermore, because stores are independent, you can colocate state logic next to the features that use it, rather than in one gigantic, central file. This leads to code that’s easier to reason about and maintain.
I encourage you to try it. Start with a small piece of global state in your next Next.js project—a user notification system, a search filter, or a modal’s open state. Create a store, use it in a couple of components, and feel the difference. You might find, as I did, that it removes a subtle friction from development, letting you focus more on building features and less on managing the plumbing.
What piece of state in your current project is causing the most prop drilling? Could it live in a simple, dedicated store instead? Give Zustand a chance to simplify it. If this approach to state management clicks for you, share it with a teammate who’s wrestling with a complex Context setup. Let’s build simpler, more maintainable applications together. If you found this walk-through helpful, please like, share, or leave a comment below with your own experiences or questions.
As a best-selling author, I invite you to explore my books on Amazon. Don’t forget to follow me on Medium and show your support. Thank you! Your support means the world!
101 Books
101 Books is an AI-driven publishing company co-founded by author Aarav Joshi. By leveraging advanced AI technology, we keep our publishing costs incredibly low—some books are priced as low as $4—making quality knowledge accessible to everyone.
Check out our book Golang Clean Code available on Amazon.
Stay tuned for updates and exciting news. When shopping for books, search for Aarav Joshi to find more of our titles. Use the provided link to enjoy special discounts!
📘 Checkout my latest ebook for free on my channel!
Be sure to like, share, comment, and subscribe to the channel!
Our Creations
Be sure to check out our creations:
Investor Central | Investor Central Spanish | Investor Central German | Smart Living | Epochs & Echoes | Puzzling Mysteries | Hindutva | Elite Dev | JS Schools
We are on Medium
Tech Koala Insights | Epochs & Echoes World | Investor Central Medium | Puzzling Mysteries Medium | Science & Epochs Medium | Modern Hindutva