Introduction
State management is one of the cornerstones of React applications. As your app grows in complexity, managing the state efficiently can become a challenge. In large applications, tools like Redux or Context API might seem over-complicated, with a lot of boilerplate code and performance concerns.
In this article, we’ll explore Zustand, a minimalistic state management library, and integrate it with the App Router in Next.js (version 13+). Zustand offers a simple and flexible approach to managing global state without the overhead of Redux or Context API, while also being well-suited for modern Next.js applications.
By the end of this article, you'll have a clear understanding of how Zustand works with the App Router and be ready to implement it in your own projects.
What is Zustand?
Zustand is a lightweight state management library that simplifies state handling in React applications. With no reducers or actions, Zustand enables easy state management by directly creating stores. It’s a great fit for applications where you want to avoid the complexity of Redux but still need a global state solution.
Key Benefits of Zustand:
- Minimal boilerplate: No actions, reducers, or providers needed.
- Performance-focused: Components only re-render when the specific parts of the state they subscribe to change.
- Simple API: Easy to integrate with any React application, including Next.js.
Setting Up Zustand with the App Router in Next.js
Setting up Zustand with Next.js using the App Router is very straightforward. The App Router is the default for new Next.js apps, leveraging the new file-system-based routing and support for server-side rendering.
1. Install Zustand
Start by installing Zustand in your Next.js app:
npm install zustand
2. Create a Zustand Store
Zustand allows you to create a store that holds all of your global state. Here’s an example of a store that manages a simple counter.
In Next.js (App Router), it’s recommended to keep the store outside the pages or app directory, typically in a lib or stores directory.
Create a store.js file in a lib folder:
import { create } from 'zustand';
// Note: 'create' as a default export is a deprecated import.
const useStore = create((set, get) => ({
count: 0,
increment: () => set((state) => ({ count: get().count + 1 })),
}));
export default useStore;
create
is used to define the store.
The store holds the count state, and increment is a function to update the count.
3. Using the Store in the App Router
With Zustand, you can use your store directly in any component or page. Here’s how to set up components to use the store.
Let's define the main page of the app as app/page.tsx
for the sake of the example.
import { useStore } from '@/lib/store'; // The store we defined earlier
import Link from 'next/link';
export default function Home() {
const { count, increment } = useStore();
return (
<div>
<h1>Home Page</h1>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
<Link href="/page2">Go to Second Page</Link>
</div>
);
};
Since Zustand's store is persistent across pages, we can create another page app/page2.tsx
and the state will be kept and change for both pages:
import { useStore } from '@/lib/store'
export default function SecondPage() {
const { count, increment } = useStore();
return (
<div>
<h1>Second Page Page</h1>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
<Link href="/">Go to Home Page</Link>
</div>
);
};
Persisting State with Zustand
You can use Zustand to persist parts of your state across browser sessions. Here’s an example where we persist the darkMode setting to localStorage:
// lib/store.ts
import { create } from 'zustand';
import { persist, createJSONStorage } from 'zustand/middleware'
const usePersistentStore = create(
persist((set) => ({
darkMode: false,
toggleDarkMode: () => set((state) => ({ darkMode: !state.darkMode })),
}),
{ name: 'persistent-store' } // Keep the store persistent on localStorage, a storage prop is optional (localStorage chosen by default)
),
);
export default useStore;
This way, even after the user refreshes or closes the app, the darkMode state remains in localStorage.
Handling Async Actions with Zustand
You can handle async actions, such as fetching data from an API, by using async functions within your Zustand store. Here’s an example:
// lib/store.ts
import { create } from 'zustand';
const useStore = create((set) => ({
data: null,
fetchData: async () => {
const response = await fetch('/api/data');
const result = await response.json();
set({ data: result });
},
}));
export default useStore;
Now, you can call fetchData from any component, and Zustand will manage the async state without any extra complexity.
Advanced Store Configuration
Zustand also allows you to create multiple stores for different concerns or use middleware for state persistence, logging, etc. You can encapsulate store logic for better organization in larger apps.
Why Zustand for Next.js?
Simplified State Logic - Zustand is a minimalistic solution that doesn’t require defining actions, reducers, or wrapping components with providers. It simplifies the state logic in a way that makes it easy to use in any Next.js app.
Optimized for Performance - Zustand is highly optimized for performance, ensuring that components only re-render when the specific part of the state they’re subscribed to changes. This prevents unnecessary re-renders and keeps your app fast and responsive.
Seamless SSR & SSG Integration - Zustand works seamlessly with Next.js's SSR and SSG features. Since Zustand stores are just JavaScript objects, you can use them directly in both server-side and client-side components without additional configuration.
Conclusion
Zustand is a great state management solution for React and Next.js applications, especially when using the App Router. Its minimalistic design, combined with its easy-to-use API, makes it ideal for both small and large-scale applications. Whether you're building a simple app or a complex system, Zustand allows you to manage state with less boilerplate and better performance.
If you’re working with Next.js 13+ and want an efficient way to manage state in your app, Zustand is definitely worth considering. Try integrating it into your projects and let me know how it works for you!
Happy coding (っ◕‿◕)っ
Zustand makes state management in React & Next.js effortless, with minimal boilerplate and maximum performance. Perfect for modern web apps! 🚀