# How the `.geolibre.json` Project File Schema Works: A Complete Technical Guide

> Explore the .geolibre.json project file schema and understand how it serializes the GeoLibre application state. Save, share, and reopen projects seamlessly.

- Repository: [Open Geospatial Solutions/GeoLibre](https://github.com/opengeos/GeoLibre)
- Tags: deep-dive
- Published: 2026-08-01

---

**The [`.geolibre.json`](https://github.com/opengeos/GeoLibre/blob/main/.geolibre.json) file is a single JSON document that serializes the entire GeoLibre application state—map view, layers, styles, plugins, and settings—enabling projects to be saved, shared, and reopened without configuration loss.**

The [`.geolibre.json`](https://github.com/opengeos/GeoLibre/blob/main/.geolibre.json) schema powers the open-source **opengeos/GeoLibre** project as its core persistence mechanism. Understanding this schema is essential for developers building integrations, automating workflows, or extending the platform with custom plugins.

## Core Schema Structure

The [`.geolibre.json`](https://github.com/opengeos/GeoLibre/blob/main/.geolibre.json) file follows a flat-top design with eight logical sections. Each section maps directly to TypeScript interfaces defined in [`packages/core/src/types.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/core/src/types.ts) and [`packages/core/src/project.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/core/src/project.ts).

### Metadata and Versioning

Every project file includes human-readable identifiers and schema versioning for forward compatibility:

- `name` – Project display name
- `description` – Optional project summary
- `createdAt` / `updatedAt` – ISO 8601 timestamps
- `version` – Semantic version of the schema (e.g., `"1.0.0"`)

The `parseProject` function in [`packages/core/src/project.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/core/src/project.ts) validates the `version` field and applies migration transforms when loading older project files.

### Map View State

The `view` object captures the camera position for session restoration:

```json
{
  "view": {
    "center": [-122.4194, 37.7749],
    "zoom": 12,
    "bearing": 0,
    "pitch": 0
  }
}

```

- **`center`** – Array of `[longitude, latitude]` in WGS84
- **`zoom`** – Web Mercator zoom level (typically 0–22)
- **`bearing`** – Rotation in degrees clockwise from north (optional)
- **`pitch`** – Tilt in degrees (0 = perpendicular to surface, optional)

This structure is defined in [`packages/core/src/types.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/core/src/types.ts) as the `MapViewState` interface.

### Basemap Configuration

The `basemap` property specifies the default background layer:

```json
{
  "basemap": {
    "type": "vector",
    "source": {
      "type": "vector",
      "tiles": ["https://demotiles.maplibre.org/style.json"]
    }
  }
}

```

Supported `type` values are `"raster"` and `"vector"`. The `source` object follows the MapLibre GL JS source specification.

## Layers and Data Sources

The `layers` array is the most complex section, containing `GeoLibreLayer` objects that define all operational data layers.

### Layer Object Properties

Each layer in the `layers` array includes these required and optional fields:

| Property | Type | Description |
|----------|------|-------------|
| `id` | `string` | Unique identifier across the project |
| `type` | `"vector"` \| `"raster"` \| `"tile"` \| `"geojson"` \| ... | Layer data type |
| `source` | `object` | Data source definition (URL, inline GeoJSON, DuckDB-WASM query, etc.) |
| `style` | `string` \| `object` | Reference to style library entry or inline paint/layout rules |
| `visibility` | `"visible"` \| `"none"` | Whether the layer renders on the map |
| `metadata` | `Record<string, unknown>` | Custom key-value pairs for application use |

### Supported Source Types

The `source` object adapts to multiple data backends:

- **Remote tiles** – Standard `{z}/{x}/{y}` tile URLs
- **GeoJSON** – Inline FeatureCollection or URL reference
- **DuckDB-WASM** – SQL query against in-browser analytical database
- **Local files** – File paths (desktop builds only)

Layer definitions are validated against the `GeoLibreLayer` interface in [`packages/core/src/types.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/core/src/types.ts).

## Style Library System

GeoLibre separates visual styling from data through the `styleLibrary` array:

```json
{
  "styleLibrary": [
    {
      "id": "choropleth-blue",
      "type": "fill",
      "paint": {
        "fill-color": ["interpolate", ["linear"], ["get", "value"], 0, "#ffffff", 100, "#0066cc"],
        "fill-opacity": 0.7
      }
    }
  ]
}

```

Layers reference styles by `id` via their `style` property, enabling:
- Consistent visual themes across multiple layers
- Single-point updates for styling changes
- Reusable style packs distributed as JSON modules

The style library is persisted in [`packages/core/src/store.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/core/src/store.ts) and hydrated into the Zustand store on project load.

## Plugin State Architecture

Plugins extend the schema through the `plugins` record:

```json
{
  "plugins": {
    "timelapse": {
      "enabled": true,
      "currentDate": "2024-01-15",
      "playbackSpeed": 1
    },
    "annotations": {
      "features": [...]
    }
  }
}

```

Each plugin implements two lifecycle methods:
- **`getProjectState()`** – Returns a serializable subset of plugin state
- **`applyProjectState(state)`** – Hydrates plugin state from the [`.geolibre.json`](https://github.com/opengeos/GeoLibre/blob/main/.geolibre.json) file

Plugin implementations reside in `packages/plugins/src/plugins/*` with type-safe state contracts.

## Environment Variables

The `env` section stores plaintext configuration values:

```json
{
  "env": {
    "MAPBOX_ACCESS_TOKEN": "pk.ey...",
    "CUSTOM_API_ENDPOINT": "https://api.example.com/v1"
  }
}

```

**Security note:** The GeoLibre UI warns users when exporting projects containing `env` values, as these may include secrets. For production workflows, use runtime environment injection instead of persisted tokens.

## Map Settings and UI Preferences

The `settings` object captures user interface state:

| Property | Description |
|----------|-------------|
| `theme` | `"light"` \| `"dark"` \| `"system"` |
| `language` | IETF language tag (e.g., `"en-US"`, `"es"`) |
| `controls` | Enabled map controls (scale, attribution, geolocate, etc.) |

These values are applied immediately on project load via `useAppStore.getState().loadProject()`.

## Project Lifecycle: Load, Modify, Save

### Loading a Project

The entry point for project deserialization is `parseProject()` in [`packages/core/src/project.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/core/src/project.ts):

```typescript
// Load from remote URL
import { parseProject, useAppStore } from "@geolibre/core";

async function loadProjectFromUrl(url: string): Promise<void> {
  const response = await fetch(url);
  const json = await response.json();
  
  // Validates version, migrates legacy formats, returns typed object
  const project = parseProject(json);
  
  // Replaces entire Zustand store state
  useAppStore.getState().loadProject(project);
}

```

For desktop builds, the `url` query parameter handler in [`apps/geolibre-desktop/src/lib/project-url.ts`](https://github.com/opengeos/GeoLibre/blob/main/apps/geolibre-desktop/src/lib/project-url.ts) performs this loading sequence when the application launches.

### Serializing Current State

Project saving uses `serializeProject()` to extract a consistent snapshot:

```typescript
import { serializeProject, useAppStore } from "@geolibre/core";
import { writeFile } from "@tauri-apps/api/fs";

async function saveToDisk(path: string): Promise<void> {
  // Extracts complete store state with plugin contributions
  const project = serializeProject(useAppStore.getState());
  
  // Pretty-printed JSON with 2-space indentation
  const json = JSON.stringify(project, null, 2);
  
  await writeFile({ path, contents: json });
}

```

The Zustand store in [`packages/core/src/store.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/core/src/store.ts) implements debounced auto-save for desktop environments, triggering `serializeProject` 500ms after state mutations cease.

### Embedding and Sharing

The [`.geolibre.json`](https://github.com/opengeos/GeoLibre/blob/main/.geolibre.json) format enables single-file project distribution:

- **Share service** – Upload generates `https://share.geolibre.app/<id>.geolibre.json`
- **URL parameter** – `https://app.geolibre.io/?url=<encoded_share_url>`
- **Embed bridge** – `useEmbedBridge` in [`apps/geolibre-desktop/src/hooks/useEmbedBridge.ts`](https://github.com/opengeos/GeoLibre/blob/main/apps/geolibre-desktop/src/hooks/useEmbedBridge.ts) loads projects without full page reload

This architecture supports use cases from classroom tutorials to production dashboard embedding.

## Accessing Plugin State Programmatically

Read or modify persisted plugin data through the store:

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

function readTimelapseConfiguration(): unknown {
  const state = useAppStore.getState();
  return state.plugins.timelapse?.state ?? {};
}

function updateAnnotationFeatures(features: GeoJSON.Feature[]): void {
  useAppStore.setState((state) => ({
    plugins: {
      ...state.plugins,
      annotations: {
        ...(state.plugins.annotations ?? {}),
        features
      }
    }
  }));
}

```

Plugin state updates follow standard Zustand patterns and are automatically included in the next `serializeProject` call.

## Key Source Files

| File Path | Responsibility |
|-----------|---------------|
| [`packages/core/src/project.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/core/src/project.ts) | `GeoLibreProject` type definition, `parseProject()`, `serializeProject()` |
| [`packages/core/src/types.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/core/src/types.ts) | TypeScript interfaces for layers, view, basemap, settings, and style library |
| [`packages/core/src/store.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/core/src/store.ts) | Zustand store implementation with `loadProject()` action |
| [`docs/project-format.md`](https://github.com/opengeos/GeoLibre/blob/main/docs/project-format.md) | Human-readable schema reference documentation |
| [`apps/geolibre-desktop/src/lib/project-url.ts`](https://github.com/opengeos/GeoLibre/blob/main/apps/geolibre-desktop/src/lib/project-url.ts) | URL parameter parsing and remote project loading |
| [`apps/geolibre-desktop/src/hooks/useEmbedBridge.ts`](https://github.com/opengeos/GeoLibre/blob/main/apps/geolibre-desktop/src/hooks/useEmbedBridge.ts) | Embedded iframe communication protocol |
| `packages/plugins/src/plugins/*` | Plugin state contracts and persistence implementations |

## Summary

- The **[`.geolibre.json`](https://github.com/opengeos/GeoLibre/blob/main/.geolibre.json) file** is a single JSON document that fully describes a GeoLibre project, from map position to plugin configurations.
- The **schema** divides into eight sections: metadata, view, basemap, layers, style library, plugins, environment variables, and settings.
- **Source code** in [`packages/core/src/project.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/core/src/project.ts) and [`packages/core/src/types.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/core/src/types.ts) defines the TypeScript contracts and validation logic.
- **`parseProject()`** and **`serializeProject()`** handle conversion between JSON and the runtime Zustand store state.
- **Plugin architecture** allows third-party extensions to persist custom state within the standard project file structure.

## Frequently Asked Questions

### What file extension should GeoLibre projects use?

GeoLibre projects must use the [`.geolibre.json`](https://github.com/opengeos/GeoLibre/blob/main/.geolibre.json) extension. The desktop application registers this extension for system-level file associations, and the web application validates the MIME type `application/json` with this specific suffix pattern during drag-and-drop imports.

### Can [`.geolibre.json`](https://github.com/opengeos/GeoLibre/blob/main/.geolibre.json) files from older versions be opened in newer GeoLibre releases?

Yes. The `parseProject()` function in [`packages/core/src/project.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/core/src/project.ts) inspects the `version` field and applies sequential migration transforms. Projects without a version field are assumed to be schema version `0.1.0` and upgraded automatically. Breaking schema changes are rare and documented in release notes with manual migration guides.

### How do I add a custom plugin's state to the project file?

Implement the `ProjectPersistence` interface in your plugin: define `getProjectState()` to return a JSON-serializable object, and `applyProjectState(state)` to restore from that object. Register your plugin with `usePluginRegistry()`, and its state will automatically appear under the `plugins.<yourPluginId>` key in exported [`.geolibre.json`](https://github.com/opengeos/GeoLibre/blob/main/.geolibre.json) files.

### Is it safe to commit [`.geolibre.json`](https://github.com/opengeos/GeoLibre/blob/main/.geolibre.json) files to version control?

Generally yes, with precautions. The `env` section may contain secrets—use `.gitignore` patterns for projects with API tokens, or implement a pre-commit hook that strips sensitive keys. The `layers` section may reference local file paths that won't resolve on other machines; prefer URL-based sources for portable projects.