Skip to main content
As your application grows, your Zustand store can become large and difficult to maintain. The slices pattern helps you divide your store into smaller, modular pieces.

Why Use Slices?

Modularity

Keep related state and actions together in isolated slices

Maintainability

Easier to understand and modify individual features

Reusability

Share slices across multiple stores

Organization

Clear separation of concerns for large applications

Creating Individual Slices

Each slice is a function that returns a partial state and actions:
1

Create the Fish Slice

fishSlice.js
export const createFishSlice = (set) => ({
  fishes: 0,
  addFish: () => set((state) => ({ fishes: state.fishes + 1 })),
})
2

Create the Bear Slice

bearSlice.js
export const createBearSlice = (set) => ({
  bears: 0,
  addBear: () => set((state) => ({ bears: state.bears + 1 })),
  eatFish: () => set((state) => ({ fishes: state.fishes - 1 })),
})
Notice that eatFish updates the fishes property from another slice. Slices can interact with each other!
3

Combine into a Bounded Store

useBoundStore.js
import { create } from 'zustand'
import { createBearSlice } from './bearSlice'
import { createFishSlice } from './fishSlice'

export const useBoundStore = create((...a) => ({
  ...createBearSlice(...a),
  ...createFishSlice(...a),
}))
The ...a syntax passes all arguments (set, get, api) to each slice creator.

Using Slices in Components

import { useBoundStore } from './stores/useBoundStore'

function App() {
  const bears = useBoundStore((state) => state.bears)
  const fishes = useBoundStore((state) => state.fishes)
  const addBear = useBoundStore((state) => state.addBear)
  
  return (
    <div>
      <h2>Number of bears: {bears}</h2>
      <h2>Number of fishes: {fishes}</h2>
      <button onClick={() => addBear()}>Add a bear</button>
    </div>
  )
}

export default App

Cross-Slice Updates

Slices can update multiple parts of the store using get() to access other slices:
createBearFishSlice.js
export const createBearFishSlice = (set, get) => ({
  addBearAndFish: () => {
    get().addBear()
    get().addFish()
  },
})
Combine it with other slices:
useBoundStore.js
import { create } from 'zustand'
import { createBearSlice } from './bearSlice'
import { createFishSlice } from './fishSlice'
import { createBearFishSlice } from './createBearFishSlice'

export const useBoundStore = create((...a) => ({
  ...createBearSlice(...a),
  ...createFishSlice(...a),
  ...createBearFishSlice(...a),
}))
Use get() to access the current state and call actions from other slices. This is useful for coordinating updates across multiple slices.

Adding Middlewares

Apply middlewares only to the combined store, not to individual slices. Applying middlewares to slices can lead to unexpected issues.
Example with the persist middleware:
useBoundStore.js
import { create } from 'zustand'
import { persist } from 'zustand/middleware'
import { createBearSlice } from './bearSlice'
import { createFishSlice } from './fishSlice'

export const useBoundStore = create(
  persist(
    (...a) => ({
      ...createBearSlice(...a),
      ...createFishSlice(...a),
    }),
    { name: 'bound-store' },
  ),
)

TypeScript Support

For TypeScript users, the slices pattern requires proper type annotations:
import { create, StateCreator } from 'zustand'

interface BearSlice {
  bears: number
  addBear: () => void
  eatFish: () => void
}

interface FishSlice {
  fishes: number
  addFish: () => void
}

const createBearSlice: StateCreator<
  BearSlice & FishSlice,
  [],
  [],
  BearSlice
> = (set) => ({
  bears: 0,
  addBear: () => set((state) => ({ bears: state.bears + 1 })),
  eatFish: () => set((state) => ({ fishes: state.fishes - 1 })),
})

const createFishSlice: StateCreator<
  BearSlice & FishSlice,
  [],
  [],
  FishSlice
> = (set) => ({
  fishes: 0,
  addFish: () => set((state) => ({ fishes: state.fishes + 1 })),
})

const useBoundStore = create<BearSlice & FishSlice>()((...a) => ({
  ...createBearSlice(...a),
  ...createFishSlice(...a),
}))
A detailed TypeScript guide for the slices pattern is available in the TypeScript Guide.

Best Practices

Group related state and actions together in the same slice. For example, put all user-related state in a userSlice, all cart-related state in a cartSlice, etc.
Each slice should have a single responsibility. If a slice becomes too large, consider splitting it further.
Name slices based on what they manage: createAuthSlice, createCartSlice, createUserSlice, etc.
Be careful when slices reference each other. If slice A needs data from slice B, use get() to access it, but avoid creating circular update loops.

File Structure Example

src/
├── stores/
│   ├── slices/
│   │   ├── bearSlice.js
│   │   ├── fishSlice.js
│   │   └── userSlice.js
│   └── useBoundStore.js
└── components/
    └── App.jsx

TypeScript Guide

Learn advanced TypeScript patterns for slices

Persist Middleware

Persist your sliced store to localStorage

Build docs developers (and LLMs) love