Skip to main content
โšก Calmops

JavaScript State Management 2026: Zustand, Jotai, Signals

Introduction

State management in JavaScript has evolved beyond Redux. New solutions like Zustand and Jotai offer simpler APIs with better performance.

This guide covers modern state management in 2026.

State Management Options

Comparison

Library Type Boilerplate Performance
Redux Toolkit Global Medium Good
Zustand Global Low Excellent
Jotai Atomic Low Excellent
Signals Reactive Very Low Excellent
useState Local None Good

Zustand

Installation

npm install zustand

Creating Store

import { create } from 'zustand';

const useStore = create((set) => ({
  count: 0,
  user: null,
  
  // Actions
  increment: () => set((state) => ({ count: state.count + 1 })),
  decrement: () => set((state) => ({ count: state.count - 1 })),
  setUser: (user) => set({ user }),
  
  // Async actions
  fetchUser: async (id) => {
    const response = await fetch(`/api/users/${id}`);
    const user = await response.json();
    set({ user });
  },
}));

Using Store

import { useStore } from './store';

function Counter() {
  const { count, increment } = useStore();
  
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={increment}>Increment</button>
    </div>
  );
}

Jotai

Installation

npm install jotai

Atomic State

import { atom } from 'jotai';

const countAtom = atom(0);
const userAtom = atom(null);
const derivedAtom = atom((get) => get(countAtom) * 2);

Using Atoms

import { useAtom } from 'jotai';

function Counter() {
  const [count, setCount] = useAtom(countAtom);
  
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(c => c + 1)}>
        Increment
      </button>
    </div>
  );
}

Signals

Preact Signals

npm install @preact/signals

Usage

import { signal, computed, effect } from '@preact/signals';

const count = signal(0);
const doubled = computed(() => count.value * 2);

// React component
function Counter() {
  return (
    <div>
      <p>Count: {count}</p>
      <p>Doubled: {doubled}</p>
      <button onClick={() => count.value++}>
        Increment
      </button>
    </div>
  );
}

Redux Toolkit (Modern Redux)

Setup

npm install @reduxjs/toolkit react-redux

Creating Slice

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

const counterSlice = createSlice({
  name: 'counter',
  initialState: { value: 0 },
  reducers: {
    increment: (state) => { state.value += 1; },
    decrement: (state) => { state.value -= 1; },
    incrementByAmount: (state, action) => {
      state.value += action.payload;
    },
  },
});

export const { increment, decrement, incrementByAmount } = counterSlice.actions;

const store = configureStore({
  reducer: counterSlice.reducer,
});

Using Redux

import { useDispatch, useSelector } from 'react-redux';
import { increment } from './counterSlice';

function Counter() {
  const count = useSelector((state) => state.counter.value);
  const dispatch = useDispatch();
  
  return (
    <button onClick={() => dispatch(increment())}>
      {count}
    </button>
  );
}

Best Practices

When to Use What

  • Local state: useState, useReducer
  • Global simple state: Zustand
  • Atomic/derived state: Jotai
  • Reactive performance: Signals
  • Large complex apps: Redux Toolkit

Performance Tips

// Zustand - selector
const count = useStore((state) => state.count);

// Jotai - derived atom
const doubledAtom = atom((get) => get(countAtom) * 2);

// Redux - memoized selector
const selectDoubled = createSelector(
  [(state) => state.counter.value],
  (count) => count * 2
);

Conclusion

Modern state management offers many options. Choose based on your needs: Zustand for simplicity, Jotai for atomic state, Signals for performance.

Resources

Comments