What Are the Key State Modules in GeoLibre's Zustand Store?

GeoLibre organizes its application state into ten distinct modules within a centralized Zustand store located at packages/core/src/store.ts, covering everything from project metadata and map views to layer management and real-time collaboration.

The open-source GeoLibre project (opengeos/GeoLibre) manages complex GIS application state using a single, undo-aware Zustand store. Understanding these key state modules is essential for developers building plugins, custom UI components, or integrations with the core mapping library.

Project and Metadata State

The Project & Metadata module handles application-level persistence and file operations. Defined in packages/core/src/store.ts around lines 81-106, this slice tracks projectName, projectPath, projectGeneration, and isDirty flags.

Key actions include newProject, loadProject, setProjectName, and markSaved. The module also maintains recentProjects for quick access to previously opened files and attributeFilter for table-level filtering across vector data.

Map View and Basemap Configuration

The Map View & Basemap module controls the camera and background imagery state. Located at lines 84-92 in store.ts, it stores mapView (center, zoom, bearing), basemapStyleUrl, basemapVisible, and basemapOpacity.

Available actions include setMapView, setBasemapStyleUrl, and specialized planetary mapping functions: applyPlanetaryBasemap and restoreEarthBasemap. This separation allows the application to switch between Earth and extraterrestrial projections while maintaining independent view states.

Layer and Group Management

The Layers & Groups module forms the data backbone of the application. Spanning lines 88-104 in store.ts, this slice maintains layers, layerGroups, layerLibrary, styleLibrary, projectStyleLibrary, and templateLibrary.

Actions follow CRUD patterns with addLayer, removeLayer, updateLayer, and setLayerVisibility. Group operations include addLayerGroup, removeLayerGroup, and moveLayerToGroup. Style persistence is handled through saveStyleLibraryEntry, allowing reusable symbology across projects.

Advanced Map Layout and Selection

Multi-Pane Grid State

The Map Grid & Multi-Pane module enables multi-viewport comparisons at lines 124-138. It stores mapLayout (grid configuration), secondaryMapViews (additional viewports), and primaryMapLabel.

Actions like setMapGrid, setSyncView, setSecondaryMapView, and removeSecondaryMapView allow synchronized or independent navigation across multiple map panes, useful for side-by-side historical imagery comparisons or multi-scale analysis.

Selection and Identify Operations

The Selection & Identify module at lines 138-147 manages interactive query state through selectedLayerId, selectedFeatureId, selectedFeatureIds, identifyLayerId, and pointerCoords.

Actions include selectLayer, selectFeature, selectFeatures, setIdentifyLayer, and setPointerCoords. This module bridges the gap between raw map interactions and the application's attribute table and info panels.

UI State and Collaboration

UI Visibility Flags

The UI Visibility module (lines 180-210) consolidates hundreds of boolean flags into a ui sub-object. Properties include processingOpen, conversionOpen, vectorToolOpen, dashboardOpen, and storymapPanelOpen.

Rather than scattered component state, these are controlled through actions like setProcessingOpen, setConversionOpen, and setDashboardOpen, enabling cross-component coordination and restoration of complex workspace layouts.

Real-Time Collaboration

The Collaboration slice at lines 196-210 handles live session state including participant presence, chat history, and session metadata. State properties live within a collaboration sub-object.

Actions include setCollaboration, updateCollaborationPresence, addCollaborationChat, and resetCollaboration. This module integrates with WebRTC or WebSocket backends to provide multi-user editing capabilities without conflicting with local undo history.

Specialized Workflows

Processing and Model History

The Processing History & Models module tracks geoprocessing operations and ML models at lines 190-201. It stores processingHistory, models, widgets, and dashboardColumns.

Actions like addProcessingRun, clearProcessingHistory, saveModel, and setDashboardColumns enable audit trails of spatial analyses and persistent widget configurations for data dashboards.

Story Map State

The Story Map module (overlapping lines 190-200) maintains narrative configuration through storymap properties and related UI flags.

Actions include setStorymap, addStoryChapter, and setStorymapPanelOpen, allowing authors to construct scroll-driven or slide-based geospatial narratives while maintaining the underlying map view state.

GPS Status

The GPS Status slice (lines 150-152) provides a minimal, focused state for mobile and field data collection through the gpsStatus property.

Updated via setGpsStatus, this feeds real-time location fixes into the status bar and geolocation-based tools without triggering unnecessary re-renders of the full map component.

Accessing the Store in Practice

The store is exposed through the useAppStore hook created with Zustand and the temporal middleware from zundo for undo/redo support:

// packages/core/src/store.ts
export const useAppStore = create<AppState>()(
  temporal(
    (set, get) => ({
      // …initial state & actions
    })
  )
);

Reading State

Access specific slices to prevent unnecessary re-renders:

import { useAppStore } from "@geolibre/core";

const basemapUrl = useAppStore((s) => s.basemapStyleUrl);
const selectedLayer = useAppStore((s) => s.selectedLayerId);

Updating Map View

Programmatically control the camera and mark the project as modified:

useAppStore.getState().setMapView(
  { center: [12.49, 41.89], zoom: 12 },
  true   // markDirty = true
);

Adding Vector Layers

Import GeoJSON programmatically from plugins or custom handlers:

import type { FeatureCollection } from "geojson";

const layerId = useAppStore.getState().addGeoJsonLayer(
  "My GeoJSON",
  myFeatureCollection as FeatureCollection,
  "/path/to/source.geojson"
);

Managing Layer Groups

Organize data hierarchically:

const groupId = useAppStore.getState().addLayerGroup("My Group");
useAppStore.getState().moveLayerToGroup(layerId, groupId);

Beyond store.ts, the state architecture relies on several supporting modules:

Summary

  • GeoLibre's state resides in a single Zustand store at packages/core/src/store.ts with undo capabilities via zundo
  • Ten distinct modules separate concerns: Project Metadata, Map View, Layers/Groups, Grid Layout, Selection, UI Flags, Collaboration, Processing History, Story Maps, and GPS
  • Each module exposes typed actions that update specific slices while maintaining immutability of unrelated state
  • The useAppStore hook supports selector optimization and direct state access via getState()
  • Supporting files in packages/core/src/ handle persistence, history management, and default styling

Frequently Asked Questions

How does GeoLibre handle undo/redo with Zustand?

GeoLibre wraps the store creator with the temporal middleware from zundo (as seen in packages/core/src/store.ts and history.ts). This tracks state changes over time and provides undo/redo functionality without manual history management. Actions that update the store automatically generate history entries, while the history.ts utilities handle coalescing rapid changes and trimming the history buffer to manage memory.

Can I subscribe to only specific state modules to optimize performance?

Yes. The useAppStore hook accepts selector functions, allowing components to subscribe to individual properties (e.g., useAppStore((s) => s.basemapStyleUrl)). This ensures components re-render only when their specific slice changes, even though all modules live in the same centralized store. The Zustand architecture handles efficient diffing and notification automatically.

Where are the TypeScript types for the state modules defined?

All type definitions reside in packages/core/src/types.ts. This includes the monolithic AppState interface that aggregates all modules, as well as supporting types like GeoLibreLayer, MapViewState, LayerGroup, and the Collaboration slice interfaces. The store file imports these types to ensure the Zustand state shape remains strictly typed throughout the application.

How does the Collaboration module synchronize between users?

The Collaboration module (lines 196-210) stores presence and chat data locally, but it relies on external transport layers (WebSockets or WebRTC) managed outside the core store. Actions like updateCollaborationPresence and addCollaborationChat update local state when remote messages arrive. The store itself remains transport-agnostic, focusing purely on state management while the application layer handles real-time synchronization protocols.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →