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

The .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 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 file follows a flat-top design with eight logical sections. Each section maps directly to TypeScript interfaces defined in packages/core/src/types.ts and 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 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:

{
  "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 as the MapViewState interface.

Basemap Configuration

The basemap property specifies the default background layer:

{
  "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.

Style Library System

GeoLibre separates visual styling from data through the styleLibrary array:

{
  "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 and hydrated into the Zustand store on project load.

Plugin State Architecture

Plugins extend the schema through the plugins record:

{
  "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 file

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

Environment Variables

The env section stores plaintext configuration values:

{
  "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:

// 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 performs this loading sequence when the application launches.

Serializing Current State

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

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 implements debounced auto-save for desktop environments, triggering serializeProject 500ms after state mutations cease.

Embedding and Sharing

The .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 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:

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 GeoLibreProject type definition, parseProject(), serializeProject()
packages/core/src/types.ts TypeScript interfaces for layers, view, basemap, settings, and style library
packages/core/src/store.ts Zustand store implementation with loadProject() action
docs/project-format.md Human-readable schema reference documentation
apps/geolibre-desktop/src/lib/project-url.ts URL parameter parsing and remote project loading
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 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 and 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 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 files from older versions be opened in newer GeoLibre releases?

Yes. The parseProject() function in 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 files.

Is it safe to commit .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.

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 →