State Management

Source: docs/guides/state-management.md

State Management

Korporus apps use Zustand for state management. Since both slot components (menubar, main) are loaded from the same MF remote, they share a single JavaScript context and can use the same Zustand store.

Basic Pattern

// src/store.ts
import { create } from "zustand";

interface MyStore {
  count: number;
  increment: () => void;
}

export const useMyStore = create((set) => ({
  count: 0,
  increment: () => set((s) => ({ count: s.count + 1 })),
}));

Using the Store in Slot Components

// src/components/MyMain.tsx
import { useMyStore } from "../store";

export function MyMain() {
  const count = useMyStore((s) => s.count);
  const increment = useMyStore((s) => s.increment);

  return (
    

Count: {count}

); }

When MyMain calls increment, the menubar component will also re-render if it subscribes to count.

Why Zustand?

  • No provider needed: Unlike React Context, Zustand stores don't require a Provider wrapper. This matters because each slot is a separate React root — they don't share a React tree.
  • Shared singleton: Zustand is declared as a shared singleton in the MF config, so all slots use the same store instance.
  • Simple API: create + useStore — no boilerplate.

State Doesn't Persist

By default, Zustand state is in-memory only. When the user navigates away from the app and back, the state resets. If you need persistence, use Zustand's persist middleware with localStorage.

System-Wide Settings

For global appearance settings (mode, theme, motion), use @korporus/system-settings instead of reading local storage directly:

import { readAppearance, subscribeAppearance } from "@korporus/system-settings";

const initialAppearance = readAppearance();
const unsubscribe = subscribeAppearance((next) => {
  // update UI based on next.mode / next.theme / next.motion
});