# How to Sync GeoLibre Layers Between the Store and MapLibre Using MapController.syncLayers

> Learn to sync GeoLibre layers between your store and MapLibre using MapController.syncLayers. Discover efficient updates for sources, layers, and styling.

- Repository: [Open Geospatial Solutions/GeoLibre](https://github.com/opengeos/GeoLibre)
- Tags: how-to-guide
- Published: 2026-08-04

---

**GeoLibre maintains map layer state in a centralized Zustand store and reconciles it with MapLibre through `MapController.syncLayers`, which computes minimal diffs via the helper logic in [`packages/map/src/layer-sync.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/map/src/layer-sync.ts) to update sources, layers, and styling efficiently.**

GeoLibre is an open-source geospatial framework that separates application state from rendering logic. The **@geolibre/core** package manages layer definitions—visibility, opacity, style, and ordering—in a reactive Zustand store, while the **@geolibre/map** package handles MapLibre rendering. When these states diverge, the `MapController.syncLayers` method bridges the gap, ensuring the visual map stays synchronized with the underlying data model.

## The Architecture of GeoLibre Layer Synchronization

### The Zustand Store as Source of Truth

The **Zustand store** defined in [`packages/core/src/store.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/core/src/store.ts) acts as the single source of truth for all layer metadata. This store maintains arrays of layer objects containing properties like `visible`, `opacity`, `zIndex`, and style definitions. When any layer property changes—whether from user interaction in the UI or programmatic updates—the store emits a subscription event that triggers the synchronization pipeline.

### MapController and the syncLayers Entry Point

The **MapController** class exposed by the **@geolibre/map** package provides the primary entry point for synchronization. Its `syncLayers` method accepts the current layer list from the store and delegates the complex diff-and-apply work to specialized helper modules. This design keeps the controller thin and testable while delegating specific implementation details to domain-specific sync handlers.

## The Eight-Step Synchronization Process

When `MapController.syncLayers` is invoked, it executes a precise pipeline to minimize DOM manipulation and maintain rendering performance:

1. **Store-driven change detection** – The Zustand store detects mutations in the `layers` slice and notifies subscribers.

2. **`MapController.syncLayers` invocation** – The controller receives the new layer array and initiates the sync cycle.

3. **Diffing in [`layer-sync.ts`](https://github.com/opengeos/GeoLibre/blob/main/layer-sync.ts)** – The helper in [`packages/map/src/layer-sync.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/map/src/layer-sync.ts) compares the current MapLibre map state against the desired store state, building three action queues:
   - **Add** – Sources and layers that exist only in the store
   - **Update** – Existing objects whose properties (style, visibility, order) changed
   - **Remove** – MapLibre objects no longer present in the store

4. **Source creation** – For vector data, the controller creates GeoJSON sources or **DuckDB-WASM** spatial sources for local files. For raster data, it instantiates `raster` sources using URLs or local MBTiles protocols.

5. **Layer creation** – Corresponding MapLibre layers (`fill`, `line`, `symbol`, `raster`, etc.) are added with styles derived from the store's `style` definitions.

6. **Ordering** – Layers are re-ordered to match the store's `zIndex` values, ensuring correct stacking of vector, raster, and UI overlay layers.

7. **Visibility and opacity updates** – The controller updates `layout.visibility` and `paint` properties to reflect the store's `visible` flag and opacity settings.

8. **Event hooks** – Post-sync hooks fire for the layer-control UI and plugins that react to new source availability.

## Implementation Details in packages/map/src/layer-sync.ts

### The Three Action Queues

The core diffing logic resides in [`packages/map/src/layer-sync.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/map/src/layer-sync.ts). This module analyzes the delta between the existing MapLibre map style and the target state defined in the store. It categorizes required changes into **Add**, **Update**, and **Remove** operations, then executes them in a specific sequence to avoid reference errors—removing obsolete layers before adding new ones, for instance.

### Specialized Layer Type Handling

Beyond standard MapLibre layers, GeoLibre supports specialized rendering engines through dedicated sync modules. The [`packages/map/src/cesium-layer-sync.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/map/src/cesium-layer-sync.ts) file handles synchronization for **Cesium-based layers**, while the plugin architecture in `packages/plugins/src/plugins/*-layer-sync.ts` (such as [`web-service-sync.ts`](https://github.com/opengeos/GeoLibre/blob/main/web-service-sync.ts)) allows third-party extensions to register custom sync handlers that integrate into the same `syncLayers` pipeline.

## Practical Code Examples

### Adding GeoJSON Layers via Store Updates

When you add a layer through the store, `syncLayers` runs automatically behind the scenes:

```typescript
import { useStore } from '@geolibre/core';
import { geoJsonLayerFromFile } from '@geolibre/processing';

const store = useStore();

async function addSampleLayer(file: File) {
  const layer = await geoJsonLayerFromFile(file);
  store.addLayer(layer);
  // MapController automatically syncs due to store subscription
}

```

### Manually Triggering MapController.syncLayers

For testing or deterministic timing, trigger synchronization explicitly:

```typescript
import { mapController } from '@geolibre/map';

function forceSync() {
  const layers = store.getState().layers;
  mapController.syncLayers(layers);
}

```

### Bulk Style Updates with Custom Sync

After modifying multiple layer properties, ensure the map reflects changes immediately:

```typescript
function updateAllOpacity(opacity: number) {
  store.updateAllLayers({ opacity });
  mapController.syncLayers(store.getState().layers);
}

```

## Summary

- **GeoLibre** uses a **Zustand store** in [`packages/core/src/store.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/core/src/store.ts) as the single source of truth for layer state.
- **`MapController.syncLayers`** orchestrates the synchronization between store state and MapLibre rendering.
- The diffing algorithm in **[`packages/map/src/layer-sync.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/map/src/layer-sync.ts)** optimizes performance by computing minimal sets of **Add**, **Update**, and **Remove** actions.
- **Source creation** handles diverse data types including GeoJSON, DuckDB-WASM, and raster/MBTiles protocols.
- **Layer ordering** respects the `zIndex` property from the store to maintain correct visual stacking.
- Specialized sync handlers exist for **Cesium** ([`cesium-layer-sync.ts`](https://github.com/opengeos/GeoLibre/blob/main/cesium-layer-sync.ts)) and plugin architectures.

## Frequently Asked Questions

### What triggers MapController.syncLayers automatically?

The method subscribes to the Zustand store's `layers` slice. Whenever you call store actions like `addLayer`, `removeLayer`, or `updateLayer`, the subscription callback executes `syncLayers` with the new state, ensuring the map updates reactively without manual intervention.

### How does the diffing algorithm in layer-sync.ts handle complex updates?

The algorithm in [`packages/map/src/layer-sync.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/map/src/layer-sync.ts) compares layer IDs and their style fingerprints between the store and the current MapLibre style specification. It batches **Add** operations for new layers, **Update** operations for modified properties like opacity or visibility, and **Remove** operations for deleted layers, applying them in a dependency-safe order to prevent dangling source references.

### Can I extend syncLayers for custom visualization engines?

Yes. GeoLibre's architecture allows you to register additional sync handlers alongside the default MapLibre implementation. You can create modules following the pattern in [`packages/map/src/cesium-layer-sync.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/map/src/cesium-layer-sync.ts) or `packages/plugins/src/plugins/*-layer-sync.ts` to handle rendering engines like Deck.gl or specialized WebGL visualizations, then invoke them within the `syncLayers` pipeline.

### Where is the layer synchronization behavior tested?

The test suite in [`tests/layer-control-style-sync.test.ts`](https://github.com/opengeos/GeoLibre/blob/main/tests/layer-control-style-sync.test.ts) validates the synchronization logic, ensuring that store mutations correctly propagate to MapLibre instances and that the diffing algorithm produces the expected map style transformations under various edge cases.