https://github.com/zustandjs/zustand-mutable
A sweet way to use immer-like mutable updates
https://github.com/zustandjs/zustand-mutable
immer limu middleware mutative zustand zustand-middleware
Last synced: 5 months ago
JSON representation
A sweet way to use immer-like mutable updates
- Host: GitHub
- URL: https://github.com/zustandjs/zustand-mutable
- Owner: zustandjs
- License: mit
- Created: 2026-01-02T22:07:57.000Z (6 months ago)
- Default Branch: main
- Last Pushed: 2026-01-04T02:03:54.000Z (6 months ago)
- Last Synced: 2026-01-13T19:44:06.836Z (5 months ago)
- Topics: immer, limu, middleware, mutative, zustand, zustand-middleware
- Language: TypeScript
- Homepage:
- Size: 102 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# zustand-mutable
[](https://github.com/zustandjs/zustand-mutable/actions?query=workflow%3ACI)
[](https://www.npmjs.com/package/zustand-mutable)
[](https://bundlephobia.com/result?p=zustand-mutable)
A sweet way to use immer-like mutable updates with Zustand.
## Introduction
Zustand's immutable state updates can become verbose when dealing with deeply nested state. This middleware lets you write state updates using a mutable API pattern, similar to Immer's draft pattern.
**Key benefit:** You choose your own produce function - use [Immer](https://github.com/immerjs/immer), [Mutative](https://github.com/unadlib/mutative), [Limu](https://github.com/tnfe/limu), or any library that follows the produce pattern.
## Installation
```bash
npm install zustand-mutable zustand
# Plus your preferred produce library (pick one):
npm install immer # Most popular
npm install mutative # Faster alternative
npm install limu # Another option
```
## Quick Start
```typescript
import { create } from 'zustand'
import { mutable } from 'zustand-mutable'
import { produce } from 'immer'
type CounterState = {
count: number
inc: () => void
}
const useStore = create()(
mutable(
(set, get) => ({
count: 0,
inc: () =>
set((state) => {
state.count = get().count + 1 // Mutate directly!
}),
}),
produce,
),
)
```
## API Reference
### `mutable(initializer, produceFn)`
Wraps your Zustand store initializer to enable mutable-style updates.
| Parameter | Type | Description |
| ------------- | ------------------------------------------------- | ------------------------------------------- |
| `initializer` | `StateCreator` | Your standard Zustand store initializer |
| `produceFn` | `(recipe: (state: T) => void) => (state: T) => T` | A produce function from your chosen library |
## Supported Libraries
### Immer
The most popular immutable state library.
```typescript
import { produce } from 'immer'
const useStore = create()(
mutable(
(set, get) => ({
// your state and actions
}),
produce,
),
)
```
### Mutative
A faster alternative to Immer with similar API.
```typescript
import { create as mutativeProduce } from 'mutative'
const useStore = create()(
mutable(
(set, get) => ({
// your state and actions
}),
mutativeProduce,
),
)
```
### Limu
Another immutable update library. Requires a wrapper function.
```typescript
import { produce } from 'limu'
const useStore = create()(
mutable(
(set, get) => ({
// your state and actions
}),
(recipe) => produce(recipe),
),
)
```
## Middleware Composition
`zustand-mutable` works seamlessly with other Zustand middleware.
### With devtools
```typescript
import { devtools } from 'zustand/middleware'
const useStore = create()(
mutable(
devtools(
(set, get) => ({
count: 0,
inc: () =>
set(
(state) => {
state.count = get().count + 1
},
false,
{ type: 'inc', by: 1 },
),
}),
{ name: 'counter' },
),
produce,
),
)
```
### With persist
```typescript
import { persist } from 'zustand/middleware'
const useStore = create()(
persist(
mutable(
(set, get) => ({
count: 0,
inc: () =>
set((state) => {
state.count = get().count + 1
}),
}),
produce,
),
{ name: 'counter-storage' },
),
)
```
### Combining Multiple Middleware
You can stack multiple middleware together:
```typescript
import { devtools, persist, subscribeWithSelector } from 'zustand/middleware'
const useStore = create()(
devtools(
subscribeWithSelector(
persist(
mutable(
(set, get) => ({
count: 0,
inc: () =>
set((state) => {
state.count = get().count + 1
}),
}),
produce,
),
{ name: 'counter' },
),
),
{ name: 'counter-devtools' },
),
)
```
## TypeScript
This library is written in TypeScript and provides full type safety out of the box.
- The `Draft` type automatically makes your state mutable inside updater functions
- Proper type inference is maintained through middleware composition
- Works with strict TypeScript configurations
```typescript
set((state) => {
// `state` is typed as Draft
// You can mutate it directly with full type safety
state.nested.deeply.value = 'new value'
})
```
## Requirements
- `zustand`
- One of: `immer`, `mutative`, or `limu`
## License
MIT