# How to Combine Multiple Stores or Use the `combine` Middleware in Zustand

> Learn how to combine multiple Zustand stores or use the combine middleware for automatic type inference and modular architecture. Enhance your state management.

- Repository: [Poimandres/zustand](https://github.com/pmndrs/zustand)
- Tags: how-to-guide
- Published: 2026-03-06

---

**Zustand provides two robust patterns for composing state: the `combine` middleware for automatic type inference and the slice pattern for modular architecture, both ultimately producing a single `StateCreator` passed to `create` or `createStore`.**

The **pmndrs/zustand** library offers flexible ways to structure complex application state without sacrificing type safety or performance. Whether you need to merge independent state slices into one unified hook or leverage the `combine` middleware to infer TypeScript types automatically, understanding these composition patterns is essential for scalable state management.

## Using the `combine` Middleware

The `combine` middleware, located in [`src/middleware/combine.ts`](https://github.com/pmndrs/zustand/blob/main/src/middleware/combine.ts), merges an initial state object with a state-creator function that adds actions. It returns a new `StateCreator` that spreads the initial object alongside the generated state, automatically inferring the final type as `Write<T, U>`—where `T` represents the initial state and `U` represents the slice.

This approach eliminates the need to manually write interfaces because TypeScript derives the complete store shape from the arguments passed to `combine`.

```typescript
// src/store/positionStore.ts
import { createStore } from 'zustand/vanilla'
import { combine } from 'zustand/middleware'

export const positionStore = createStore(
  combine(
    // Initial state
    { position: { x: 0, y: 0 } },

    // Slice adding actions
    (set) => ({
      setPosition: (pos: { x: number; y: number }) => set({ position: pos })
    })
  )
)

```

When using **vanilla stores** (outside React), `combine` works seamlessly with `createStore` to produce a store instance you can subscribe to directly.

## The Slice Pattern for Modular Stores

The **slice pattern** treats each feature module as an independent function that receives the standard `(set, get, api)` arguments and returns its own slice of state and actions. According to [`docs/learn/guides/slices-pattern.md`](https://github.com/pmndrs/zustand/blob/main/docs/learn/guides/slices-pattern.md), the final store assembles these slices by spreading their results inside a single `create` call.

This pattern excels when you want clear separation of concerns, with each slice living in its own file and potentially being imported selectively.

```typescript
// src/slices/fishSlice.ts
export const createFishSlice = (set) => ({
  fishes: 0,
  addFish: () => set((s) => ({ fishes: s.fishes + 1 })),
})

// src/slices/bearSlice.ts
export const createBearSlice = (set) => ({
  bears: 0,
  addBear: () => set((s) => ({ bears: s.bears + 1 })),
  eatFish: () => set((s) => ({ fishes: s.fishes - 1 })),
})

// src/store/useBoundStore.ts
import { create } from 'zustand'
import { createBearSlice } from './slices/bearSlice'
import { createFishSlice } from './slices/fishSlice'

export const useBoundStore = create((...a) => ({
  ...createBearSlice(...a),
  ...createFishSlice(...a),
}))

```

The rest parameters `...a` distribute the `set`, `get`, and `api` arguments to each slice, ensuring every slice operates on the same underlying store instance.

## Adding Middleware to Combined Stores

Both composition methods support wrapping with additional middleware such as `persist` or `devtools`. As noted in [`docs/reference/middlewares/combine.md`](https://github.com/pmndrs/zustand/blob/main/docs/reference/middlewares/combine.md), you should apply middleware **once** around the entire combined creator rather than inside individual slices to avoid duplicate state handling or conflicting middleware instances.

```typescript
import { create } from 'zustand'
import { persist } from 'zustand/middleware'
import { createBearSlice } from './slices/bearSlice'
import { createFishSlice } from './slices/fishSlice'

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

```

This pattern works because both `combine` and the slice pattern ultimately return a `StateCreator` function compatible with `create` and `createStore` from [`src/vanilla.ts`](https://github.com/pmndrs/zustand/blob/main/src/vanilla.ts) and [`src/react.ts`](https://github.com/pmndrs/zustand/blob/main/src/react.ts).

## Summary

- **`combine` middleware**: Located in [`src/middleware/combine.ts`](https://github.com/pmndrs/zustand/blob/main/src/middleware/combine.ts), merges initial state with action creators and automatically infers TypeScript types via `Write<T, U>`.
- **Slice pattern**: Documented in [`docs/learn/guides/slices-pattern.md`](https://github.com/pmndrs/zustand/blob/main/docs/learn/guides/slices-pattern.md), creates modular state slices that are spread into a single `create` call for clean separation of concerns.
- **Middleware compatibility**: Wrap the final combined creator with `persist`, `devtools`, or `immer` exactly once to avoid the "middleware inside a slice" anti-pattern.
- **Underlying mechanism**: Both approaches produce a `StateCreator` consumed by `create` (React) or `createStore` (vanilla) functions.

## Frequently Asked Questions

### What is the difference between `combine` and the slice pattern?

The **`combine` middleware** explicitly merges an initial state object with a function returning additional state and actions, offering automatic type inference without manual interfaces. The **slice pattern** emphasizes architectural separation, where each feature exports a factory function that receives `(set, get, api)` and returns its state slice, promoting code colocation and selective imports. Both achieve state composition but optimize for different developer workflows.

### Can I use `combine` with TypeScript?

Yes, `combine` is designed for TypeScript. It infers the complete store type as `Write<T, U>`, merging the initial state `T` with the creator's return type `U`. This eliminates the need to manually declare interfaces for the combined state, as documented in [`docs/reference/middlewares/combine.md`](https://github.com/pmndrs/zustand/blob/main/docs/reference/middlewares/combine.md).

### How do I add persistence to a combined Zustand store?

Apply the `persist` middleware once around the entire combined creator. Whether using `combine` or the slice pattern, wrap the final `StateCreator` with `persist(...)` before passing it to `create`. This ensures the middleware captures the complete state tree rather than individual slices.

### Is the slice pattern better for large applications?

The slice pattern scales well for large codebases because it enforces **modular boundaries**—each slice lives in its own file and manages a specific domain. However, `combine` offers superior ergonomics for smaller stores or when type inference is prioritized. Large applications often adopt the slice pattern for organizational clarity while still leveraging `combine` within individual slices if automatic typing is desired.