# What State Management Library Does Instatic Use? Zustand Implementation Deep Dive

> Instatic utilizes Zustand for state management, leveraging `zustand-mutative` and `subscribeWithSelector` for efficient, immutable updates in its admin interface.

- Repository: [CoreBunch/Instatic](https://github.com/CoreBunch/Instatic)
- Tags: deep-dive
- Published: 2026-07-02

---

**Instatic uses Zustand, a minimal React state container, enhanced with the `zustand-mutative` middleware for immutable draft updates and `subscribeWithSelector` for performant subscriptions, to power its admin interface.**

The CoreBunch/Instatic repository leverages Zustand as its primary state management library, implementing a slice-based architecture that supports direct state mutations through middleware while maintaining type safety across the visual editor and AI agent stores.

## Core State Management Library Configuration

The global store definition resides in [`src/admin/pages/site/store/store.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/admin/pages/site/store/store.ts), where the application composes multiple domain slices into a single unified store. The configuration uses `create` from `zustand` wrapped in two critical middlewares:

1. **`subscribeWithSelector`** from `zustand/middleware` – Enables efficient subscriptions to specific state slices
2. **`mutative`** from `zustand-mutative` – Allows draft-style mutations that compile to immutable updates

```typescript
// src/admin/pages/site/store/store.ts
import { create } from 'zustand';
import { mutative } from 'zustand-mutative';
import { subscribeWithSelector } from 'zustand/middleware';
import { siteSlice } from './slices/site';
import { settingsSlice } from './slices/settings';

export const useStore = create()(
  subscribeWithSelector,
  mutative,
  (set, get) => ({
    ...siteSlice(set, get),
    ...settingsSlice(set, get),
    // …other slices
  })
);

```

## Slice Architecture with Mutative Updates

Each domain area defines its own TypeScript slice using the `StateCreator` type from Zustand. The `siteSlice` in [`src/admin/pages/site/store/slices/site/types.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/admin/pages/site/store/slices/site/types.ts) demonstrates the mutative pattern:

```typescript
// src/admin/pages/site/store/slices/site/types.ts
import type { StateCreator } from 'zustand';
import type { Draft } from 'immer';

export type SiteState = {
  selectedNodeId: string | null;
  // …
};

export const siteSlice: StateCreator<SiteState, [['zustand/mutative', never]]> = (set) => ({
  selectedNodeId: null,
  selectNode: (id) =>
    set((state: Draft<SiteState>) => {
      state.selectedNodeId = id;      // mutable draft
    }),
});

```

The type annotation `[['zustand/mutative', never]]` marks the slice as mutative-enabled, allowing direct property assignment on the draft state while the middleware handles immutable updates under the hood.

## React Integration and Performance Patterns

Components access the store through hooks that leverage `useShallow` from `zustand/react/shallow` to prevent unnecessary re-renders. The [`usePropertiesPanelData.ts`](https://github.com/CoreBunch/Instatic/blob/main/usePropertiesPanelData.ts) utility in the PropertiesPanel illustrates this pattern:

```typescript
// src/admin/pages/site/panels/PropertiesPanel/usePropertiesPanelData.ts
import { useStore } from '../../store/store';
import { useShallow } from 'zustand/react/shallow';

export const usePropertiesPanelData = () => {
  const { selectedNodeId, selectNode } = useStore(
    useShallow((s) => ({
      selectedNodeId: s.selectedNodeId,
      selectNode: s.selectNode,
    }))
  );
  
  return { selectedNodeId, selectNode };
};

```

For imperative access outside React components, the store exposes a `getState()` method directly on the Zustand instance, as implemented in [`src/admin/ai/useAgentStore.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/admin/ai/useAgentStore.ts).

## Build Configuration and Middleware Bundling

The Vite configuration in [`vite.config.ts`](https://github.com/CoreBunch/Instatic/blob/main/vite.config.ts) explicitly treats `zustand-mutative` as a vendor dependency, ensuring proper code splitting and caching. This separation keeps the mutative middleware optimized and independently cacheable from application logic.

## Summary

- **Zustand serves as the core state management library** for Instatic's admin UI, providing a lightweight alternative to Redux or Context API.
- **The `zustand-mutative` middleware** enables ergonomic draft-style mutations while preserving immutable update semantics, replacing Immer with a smaller bundle footprint.
- **Typed slices** organize state by domain (site, settings, agents) using `StateCreator` with middleware type annotations.
- **`useShallow` selectors** optimize React rendering performance by preventing component updates when unrelated state changes.
- **Key files**: [`src/admin/pages/site/store/store.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/admin/pages/site/store/store.ts) defines the global store, [`src/admin/state/workspaceLayout.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/admin/state/workspaceLayout.ts) contains UI-level stores, and [`src/admin/pages/site/store/slices/site/types.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/admin/pages/site/store/slices/site/types.ts) demonstrates the mutative slice pattern.

## Frequently Asked Questions

### Why did Instatic choose Zustand over Redux or Context API?

Zustand provides the global state capabilities required for a complex visual editor without Redux's boilerplate or Context API's performance limitations. The `zustand-mutative` middleware specifically allows the team to write mutable-style updates that compile to immutable changes, matching Immer's ergonomics while maintaining minimal bundle size.

### How does the `zustand-mutative` middleware handle immutable updates?

The middleware intercepts mutations on draft objects and applies them as immutable updates behind the scenes. When slice writers call `set((state) => { state.selectedNodeId = id })`, the middleware creates a draft copy, applies the mutation, and returns a new immutable state to Zustand's store, ensuring React's change detection works correctly while allowing simpler syntax.

### Can Instatic's Zustand store be accessed outside of React components?

Yes. The store exports a `getState()` method directly from the Zustand instance, enabling access in utility functions and AI agents. For example, `useZustandStore.getState()` returns the current state object without requiring React hooks, as implemented in files like [`src/admin/ai/useAgentStore.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/admin/ai/useAgentStore.ts).

### What is the purpose of `subscribeWithSelector` in Instatic's store?

`subscribeWithSelector` enables components to subscribe to specific state slices rather than the entire store. This middleware improves performance by ensuring callbacks only fire when selected portions of state change, rather than on any store update, which is critical for a high-frequency editor interface where components need granular state awareness.