Skip to main content

ADR-002: Use Zustand for State Management

Date: 2025-10-06 Status: Accepted Deciders: Development Team Tags: state-management, architecture

Context​

The IDE requires complex state management across multiple features (editor, terminal, llm chat, file system). We need a solution that is simple, performant, and integrates well with React.

Decision​

We will use Zustand 4.4 as our primary state management library.

Rationale​

Why Zustand​

  1. Simplicity: Minimal boilerplate, easy to understand
  2. Performance: No provider needed, direct store access
  3. TypeScript: Excellent TypeScript support out of the box
  4. Bundle Size: ~1KB (vs Redux ~10KB)
  5. DevTools: Redux DevTools compatible
  6. Middleware: Persist, immer, devtools available
  7. Learning Curve: Minimal - just hooks

Key Features We Need​

  • Multiple stores: Separate editor, llm, file, terminal stores
  • Persistence: Session data survives refreshes
  • Async actions: llm API calls, file operations
  • No re-render overhead: Selector-based subscriptions

Alternatives Considered​

Redux Toolkit​

  • Pros: Industry standard, extensive ecosystem, mature
  • Cons: More boilerplate, provider wrapper needed, larger bundle
  • Rejected: Overkill for our use case, unnecessary complexity

Recoil​

  • Pros: Facebook-backed, atom-based, good for complex state
  • Cons: Larger bundle, requires provider, experimental status
  • Rejected: Less stable, more complex than needed

Jotai​

  • Pros: Atomic state, minimal, TypeScript-first
  • Cons: Different mental model, less documentation
  • Rejected: Zustand more established, simpler API

Context API + useReducer​

  • Pros: Built-in, no dependencies
  • Cons: Performance issues, provider hell, no persistence
  • Rejected: Not suitable for complex app-wide state

Consequences​

Positive​

  • Fast development with minimal boilerplate
  • Easy to test stores in isolation
  • Good performance with selective re-renders
  • Small bundle impact
  • Easy migration path if needed (simple API)

Negative​

  • Less "official" than Redux (community adoption)
  • Fewer learning resources than Redux
  • Middleware ecosystem smaller than Redux

Neutral​

  • Need to establish store organization patterns
  • Persistence middleware configuration required

Implementation​

Store Structure​

// editor-store.ts
interface editorStore {
tabs: editorTab[];
activeTabId: string | null;
addTab: (tab: editorTab) => void;
removeTab: (id: string) => void;
updateTab: (id: string, content: string) => void;
}

export const useeditorStore = create<editorStore>((set) => ({
tabs: [],
activeTabId: null,
addTab: (tab) => set((state) => ({
tabs: [...state.tabs, tab],
activeTabId: tab.id
})),
// ...
}));

With Persistence​

import { persist } from 'zustand/middleware';

export const usellmStore = create<llmStore>()(
persist(
(set) => ({
messages: [],
addMessage: (msg) => set((state) => ({
messages: [...state.messages, msg]
}))
}),
{ name: 'llm-storage' }
)
);

Store Organization​

src/store/
├── editor-store.ts # editor tabs, content
├── llm-store.ts # llm messages, config
├── fileStore.ts # File tree, OPFS
├── terminal-store.ts # terminal history, state
└── session-store.ts # Multi-session management *NEW*

References​