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:
- Reads
orgId,spaceId,environmentId, andimpersonateUserIdfromuiStore - Injects
X-Alokai CMS-Organization,X-Alokai CMS-Space,X-Alokai CMS-Environment, and optionallyX-Alokai CMS-Impersonateheaders - 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' });