# Zustand Store Structure for Managing Application State in Ontology-Playground

> Explore the Zustand store structure in Ontology-Playground. Discover how the app organizes state into Ontology, UI, Quest, and Query slices with dedicated actions for efficient management.

- Repository: [Microsoft/Ontology-Playground](https://github.com/microsoft/Ontology-Playground)
- Tags: internals
- Published: 2026-07-23

---

**The Ontology-Playground application uses a single Zustand store defined in [`src/store/appStore.ts`](https://github.com/microsoft/Ontology-Playground/blob/main/src/store/appStore.ts) that organizes state into four distinct slices—Ontology, UI, Quest, and Query—each with dedicated actions for immutable updates.**

The Microsoft Ontology-Playground project leverages Zustand to provide lightweight, type-safe state management for its ontology visualization and quest features. Understanding the Zustand store structure is essential for extending the application or debugging state-related issues, as the entire client-side state lives within a single `create<AppStore>` instance exported from the store module.

## Store Architecture Overview

The state management layer centers on **[`src/store/appStore.ts`](https://github.com/microsoft/Ontology-Playground/blob/main/src/store/appStore.ts)**, which exports the main application store. Unlike Redux or Context-based solutions, Ontology-Playground uses a single monolithic store that combines all state slices and their corresponding actions in one TypeScript interface.

The store initialization pulls from default data constants including `cosmicCoffeeOntology`, `sampleBindings`, and `defaultQuests`, while also hydrating theme preferences from `localStorage`. Two helper utilities, `isDarkTheme` and `themeClass`, derive computed values from the `theme` and `darkMode` flags to simplify component-level styling logic.

## State Slices and Interface

The `AppState` interface defines the contract for all mutable properties, organized into four functional domains.

### Ontology State

This slice manages the core data model and its bindings:

- **`currentOntology`** (`Ontology`): The active ontology loaded into the visual editor.
- **`dataBindings`** (`DataBinding[]`): Mappings that link ontology entities to runtime data sources.

### UI State

Interaction state for the graph visualization and interface controls:

- **`selectedEntityId`** (`string | null`): Identifies the currently selected node in the graph.
- **`selectedRelationshipId`** (`string | null`): Tracks the active edge selection.
- **`highlightedEntities`** (`string[]`): Array of entity IDs emphasized during quest guidance or search results.
- **`highlightedRelationships`** (`string[]`): Array of relationship IDs currently highlighted.
- **`showDataBindings`** (`boolean`): Toggles visibility of data-binding overlay panels.
- **`theme`** (`ThemeId`): Active theme identifier (`dark`, `light`, `aurora`, `crimson`).
- **`darkMode`** (`boolean`): Boolean flag affecting canvas rendering and syntax highlighting.

### Quest State

Gamification layer for guided learning experiences:

- **`availableQuests`** (`Quest[]`): Catalog of quest definitions available to the user.
- **`activeQuest`** (`Quest | null`): The quest currently in progress, or `null` if none.
- **`currentStepIndex`** (`number`): Zero-based index tracking position within the active quest.
- **`completedQuests`** (`string[]`): IDs of finished quests for progress tracking.
- **`earnedBadges`** (`{ badge: string; icon: string }[]`): Collection of rewards unlocked through completion.
- **`totalPoints`** (`number`): Aggregated score across all completed activities.

### Query State

SPARQL-like interaction state:

- **`queryInput`** (`string`): Raw text entered in the query editor.
- **`queryResult`** (`string | null`): Serialized output from the last executed query.

## Actions and State Mutations

All state modifications flow through action functions defined in the store creator, using Zustand's `set` and `get` parameters to ensure immutable updates.

### Ontology Actions

- **`loadOntology(ontology, bindings)`**: Replaces `currentOntology` and `dataBindings` simultaneously.
- **`resetToDefault()`**: Reverts to the initial `cosmicCoffeeOntology` state.
- **`exportOntology()`**: Serializes the current ontology for download.

### UI Actions

- **`selectEntity(id)`** and **`selectRelationship(id)`**: Update selection markers.
- **`setHighlightedEntities(ids)`** and **`setHighlightedRelationships(ids)`**: Batch-update highlight arrays.
- **`setHighlights(entities, relationships)`**: Convenience method for updating both highlight arrays atomically.
- **`toggleDataBindings()`**: Flips the `showDataBindings` boolean.
- **`setTheme(theme)`**: Updates the active theme ID.
- **`toggleDarkMode()`**: Switches the `darkMode` flag and persists to `localStorage`.

### Quest Actions

- **`startQuest(questId)`**: Initializes `activeQuest` and resets `currentStepIndex` to 0.
- **`advanceQuestStep()`**: Increments `currentStepIndex` within bounds.
- **`completeQuest()`**: Transfers the quest ID to `completedQuests`, awards badges, and clears `activeQuest`.
- **`abandonQuest()`**: Clears active quest state without awarding points.

### Query Actions

- **`setQueryInput(input)`**: Updates the query editor text.
- **`setQueryResult(result)`**: Stores serialized query output.
- **`clearHighlights()`**: Resets both entity and relationship highlight arrays.

## Usage Examples

Accessing state in React components uses the standard Zustand selector pattern:

```typescript
import { useAppStore } from '@/store/appStore';

// Subscribe to specific state slices
const theme = useAppStore(state => state.theme);
const selectedEntity = useAppStore(state => state.selectedEntityId);

// Trigger actions outside React render cycle
const toggleTheme = () => {
  useAppStore.getState().toggleDarkMode();
};

```

Loading a custom ontology with bindings:

```typescript
import { useAppStore } from '@/store/appStore';
import { myOntology } from '@/data/ontology';

// Actions accept multiple parameters
useAppStore.getState().loadOntology(myOntology, [{ /* binding objects */ }]);

```

Managing quest lifecycle:

```typescript
const store = useAppStore.getState();

// Start a guided quest
store.startQuest('quest-42');

// Progress through steps
store.advanceQuestStep();

// Complete and award badges
store.completeQuest();

```

Executing queries and visualizing results:

```typescript
const executeQuery = (queryString: string) => {
  const store = useAppStore.getState();
  store.setQueryInput(queryString);
  
  // Query execution logic against current ontology
  const result = runQuery(queryString, store.currentOntology);
  store.setQueryResult(result);
};

```

## Summary

- The **Zustand store structure** in Ontology-Playground consolidates all state in [`src/store/appStore.ts`](https://github.com/microsoft/Ontology-Playground/blob/main/src/store/appStore.ts) using a single `create<AppState>` pattern.
- State is organized into **four slices**: Ontology (data model), UI (selections and highlights), Quest (gamification), and Query (SPARQL interactions).
- All mutations occur through **typed actions** that receive `set` and `get` for immutable updates, with specific handlers for loading ontologies, managing quest progress, and toggling UI modes.
- **Default values** initialize from `cosmicCoffeeOntology` and `localStorage` persists theme preferences across sessions.

## Frequently Asked Questions

### How does Ontology-Playground handle theme persistence?

The store checks `localStorage` during initialization to hydrate the `theme` and `darkMode` values. When `toggleDarkMode()` or `setTheme()` is called, the actions update both the store state and `localStorage` to ensure preferences survive page refreshes. The `isDarkTheme` helper computes the effective dark mode state based on the current theme selection.

### Can I use multiple stores instead of the single appStore?

While the primary architecture uses a single store in [`src/store/appStore.ts`](https://github.com/microsoft/Ontology-Playground/blob/main/src/store/appStore.ts), the codebase also exports a separate **[`src/store/designerStore.ts`](https://github.com/microsoft/Ontology-Playground/blob/main/src/store/designerStore.ts)** for visual designer-specific state. This secondary store handles graph layout and canvas interactions independently, demonstrating that the application supports store composition when domain separation is necessary.

### What is the difference between selectEntity and setHighlightedEntities?

`selectEntity(id)` updates `selectedEntityId` to indicate the user's active focus in the property panel, while `setHighlightedEntities(ids)` populates `highlightedEntities` to temporarily emphasize multiple nodes during quest guidance or search results. Selection represents user intent; highlights represent transient visual cues that can be cleared via `clearHighlights()`.

### How are quest badges and points calculated?

The `completeQuest()` action automatically appends the quest's defined badges to `earnedBadges` and increments `totalPoints` by the quest's reward value. These values are stored in the `AppState` interface as plain arrays and numbers, making them serializable for future persistence implementations.