Modern State Management: Zustand, Jotai, and Beyond

State Management Evolution

The frontend world has moved beyond Redux. Modern solutions are simpler, more performant, and better suited to different use cases.

Zustand - Simple & Scalable

import { create } from 'zustand';
import { devtools, persist } from 'zustand/middleware';

interface CartState {
  items: CartItem[];
  total: number;
  addItem: (item: CartItem) => void;
  removeItem: (id: string) => void;
  clearCart: () => void;
}

export const useCartStore = create()(
  devtools(
    persist(
      (set, get) => ({
        items: [],
        total: 0,
        
        addItem: (item) => set((state) => ({
          items: [...state.items, item],
          total: state.total + item.price
        })),
        
        removeItem: (id) => set((state) => {
          const item = state.items.find(i => i.id === id);
          return {
            items: state.items.filter(i => i.id !== id),
            total: state.total - (item?.price || 0)
          };
        }),
        
        clearCart: () => set({ items: [], total: 0 })
      }),
      { name: 'cart-storage' }
    )
  )
);

// Usage in component
function CartButton() {
  const itemCount = useCartStore((state) => state.items.length);
  return ;
}

Jotai - Atomic State

import { atom, useAtom, useAtomValue, useSetAtom } from 'jotai';
import { atomWithStorage } from 'jotai/utils';

// Define atoms
const userAtom = atom(null);
const themeAtom = atomWithStorage('theme', 'light');

// Derived atoms
const isLoggedInAtom = atom((get) => get(userAtom) !== null);

// Async atom
const userProfileAtom = atom(async (get) => {
  const user = get(userAtom);
  if (!user) return null;
  const response = await fetch(`/api/users/${user.id}/profile`);
  return response.json();
});

// Write-only atom for actions
const loginAtom = atom(
  null,
  async (get, set, credentials: { email: string; password: string }) => {
    const response = await fetch('/api/login', {
      method: 'POST',
      body: JSON.stringify(credentials)
    });
    const user = await response.json();
    set(userAtom, user);
  }
);

// Usage
function UserStatus() {
  const user = useAtomValue(userAtom);
  const isLoggedIn = useAtomValue(isLoggedInAtom);
  const [theme, setTheme] = useAtom(themeAtom);
  const login = useSetAtom(loginAtom);
  
  return (...);
}

Redux Toolkit - When You Need It

import { createSlice, configureStore, createAsyncThunk } from '@reduxjs/toolkit';

// Async thunk
export const fetchUsers = createAsyncThunk(
  'users/fetch',
  async () => {
    const response = await fetch('/api/users');
    return response.json();
  }
);

const usersSlice = createSlice({
  name: 'users',
  initialState: {
    items: [] as User[],
    status: 'idle' as 'idle' | 'loading' | 'succeeded' | 'failed',
    error: null as string | null
  },
  reducers: {
    userAdded: (state, action) => {
      state.items.push(action.payload);
    }
  },
  extraReducers: (builder) => {
    builder
      .addCase(fetchUsers.pending, (state) => {
        state.status = 'loading';
      })
      .addCase(fetchUsers.fulfilled, (state, action) => {
        state.status = 'succeeded';
        state.items = action.payload;
      })
      .addCase(fetchUsers.rejected, (state, action) => {
        state.status = 'failed';
        state.error = action.error.message ?? null;
      });
  }
});

TanStack Query - Server State

import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';

// Fetch data with caching
function useUsers() {
  return useQuery({
    queryKey: ['users'],
    queryFn: () => fetch('/api/users').then(r => r.json()),
    staleTime: 5 * 60 * 1000, // 5 minutes
  });
}

// Mutations with optimistic updates
function useCreateUser() {
  const queryClient = useQueryClient();
  
  return useMutation({
    mutationFn: (newUser: CreateUserDto) =>
      fetch('/api/users', {
        method: 'POST',
        body: JSON.stringify(newUser)
      }).then(r => r.json()),
      
    onMutate: async (newUser) => {
      await queryClient.cancelQueries({ queryKey: ['users'] });
      const previous = queryClient.getQueryData(['users']);
      queryClient.setQueryData(['users'], (old: User[]) => [
        ...old,
        { ...newUser, id: 'temp' }
      ]);
      return { previous };
    },
    
    onError: (err, newUser, context) => {
      queryClient.setQueryData(['users'], context?.previous);
    },
    
    onSettled: () => {
      queryClient.invalidateQueries({ queryKey: ['users'] });
    }
  });
}

Choosing the Right Solution

SolutionBest For
ZustandSimple global state, easy migration from Redux
JotaiAtomic state, fine-grained updates
Redux ToolkitComplex state logic, middleware needs
TanStack QueryServer state, caching, sync
React ContextTheme, auth, small shared state

Conclusion

Match your state management to your needs. Use TanStack Query for server state, lightweight solutions like Zustand for client state, and Context for simple sharing.

๐Ÿ“šTechnical Insights & Industry Trends

Insights & Knowledge Hub

Explore in-depth articles on software development, digital transformation, and emerging technologies. Learn from real-world experience.

Browse by Topic

๐Ÿ…ฐ๏ธ
ArchitectureJan 2024

Why Angular SSR Beats React for SEO in 2024

A comprehensive comparison of server-side rendering implementations and their impact on search engine optimization, performance, and user experience.

๐Ÿ”Œ
ArchitectureDec 2023

Microservices: When to Use and When to Avoid

Understanding the trade-offs of microservices architecture and making the right choice for your organization's scale, complexity, and team structure.

๐Ÿ—๏ธ
System ArchitectureJan 2024

Essential Design Patterns for Enterprise Systems

Master the fundamental design patterns that every software architect should know for building scalable, maintainable enterprise applications.

๐Ÿ—๏ธ
System ArchitectureJan 2024

Domain-Driven Design: A Practical Guide

Learn how to apply Domain-Driven Design principles to create software that truly reflects business requirements and scales with organizational complexity.

๐Ÿ—๏ธ
System ArchitectureDec 2023

Event-Driven Architecture: Complete Implementation Guide

Build loosely coupled, scalable systems using event-driven architecture patterns with practical examples using Kafka, RabbitMQ, and Azure Service Bus.

๐Ÿ—๏ธ
System ArchitectureDec 2023

RESTful API Design: Best Practices and Patterns

Design APIs that developers love to use. Learn REST principles, versioning strategies, error handling, and documentation best practices.

Deep Dives into Key Areas

๐Ÿ—๏ธ

System Architecture

Design patterns and architectural decisions

12 Articles
โ˜๏ธ

Cloud Computing

AWS, Azure, and cloud-native development

8 Articles
โšก

Performance

Optimization and scalability techniques

6 Articles
๐Ÿค–

AI & Machine Learning

Practical AI applications in enterprise

5 Articles
๐Ÿ”’

Security

Best practices and compliance

7 Articles
๐Ÿ“ฑ

Modern Frontend

Angular, React, and UI/UX best practices

10 Articles

Have a Project in Mind?

Let's turn your ideas into reality. Schedule a free consultation to discuss how we can help transform your business.