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 namedescription– Optional project summarycreatedAt/updatedAt– ISO 8601 timestampsversion– 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 WGS84zoom– 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 stateapplyProjectState(state)– Hydrates plugin state from the.geolibre.jsonfile
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 –
useEmbedBridgeinapps/geolibre-desktop/src/hooks/useEmbedBridge.tsloads 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.jsonfile 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.tsandpackages/core/src/types.tsdefines the TypeScript contracts and validation logic. parseProject()andserializeProject()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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →