Skip to content

State Management

The frontend uses Zustand for global state. There are two stores.

uiStore

Manages auth state, the current organizational context, and UI flags.

Key state:

{
// Authentication
isAuthenticated: boolean
currentUser: { id: string; email: string; name: string; role: string } | null
// Organizational context (persisted to localStorage)
orgId: string
orgName: string
spaceId: string
spaceName: string
environmentId: string
environments: EnvironmentInfo[]
// UI
locale: string
previewUrl: string
isSidebarOpen: boolean
selectedComponentId: string | null
// Impersonation (super_admin only)
impersonateUserId: string | null
// Forces re-mount of space-dependent components
contextKey: number
}

Persistence: orgId, spaceId, environmentId, and locale are persisted to localStorage using Zustand’s persist middleware. This means your context is remembered across page refreshes.

Context key: When the space is switched (setSpace), contextKey is incremented. Components that should fully re-initialize on space change use this as a React key, forcing an unmount/remount.

API client integration: The lib/api.ts HTTP client reads from uiStore to automatically inject the X-Alokai CMS-* context headers on every request.

pageStore

Manages the state of the page currently being edited.

Key state:

{
page: Page | null
isDirty: boolean // unsaved changes exist
// Operations
addComponent(zone, type, data): void
updateComponent(id, data): void
removeComponent(zone, id): void
reorderComponents(zone, oldIndex, newIndex): void
setZoneComponents(zone, components): void
}

The pageStore is reset whenever you navigate to a different page or switch locale.

HTTP client (lib/api.ts)

The API client is a thin wrapper around fetch that:

  1. Reads orgId, spaceId, environmentId, and impersonateUserId from uiStore
  2. Injects X-Alokai CMS-Organization, X-Alokai CMS-Space, X-Alokai CMS-Environment, and optionally X-Alokai CMS-Impersonate headers
  3. Handles 401 responses by clearing auth state and redirecting to login
import { api } from '@/lib/api';
const pages = await api.get('/api/pages');
const page = await api.post('/api/pages', { path: '/about', content_type: 'page' });