Developing with the GeoLibre File System: A Complete Guide to `.geolibre.json`
GeoLibre stores every workspace as a single .geolibre.json file, providing a complete, portable representation of maps, layers, styles, and dashboard widgets.
This guide covers how to work with GeoLibre's file system when building applications with the opengeos/GeoLibre repository. Whether you're creating UI features, automating tests, or extending the platform, understanding the .geolibre.json format and its associated APIs is essential.
What Is the .geolibre.json File Format?
Every GeoLibre project persists to a single JSON file with the .geolibre.json extension. This file contains the entire application state: map view configuration, layer definitions, style libraries, plugin settings, story-map chapters, and dashboard widgets.
The format is documented in [docs/project-format.md](docs/project-format.md) and follows a strict schema with version tracking, enabling forward compatibility as the platform evolves.
Core Schema Structure
A minimal valid .geolibre.json includes:
version: Schema version (e.g.,"0.1.0")name: Human-readable project namemapView: Center, zoom, bearing, and pitchbasemapStyleUrl,basemapVisible,basemapOpacity: Basemap configurationlayers: Array of layer objects with source and style referencesstyles: Style library definitions
File Name Handling and Sanitization
GeoLibre enforces consistent file naming through pure utility functions in [apps/geolibre-desktop/src/lib/file-names.ts](apps/geolibre-desktop/src/lib/file-names.ts).
ensureProjectFileName()
The ensureProjectFileName function guarantees valid project file names by:
- Trimming whitespace from user input
- Appending
.geolibre.jsonif the extension is missing - Preserving existing extensions when already present
import { ensureProjectFileName } from "@/lib/file-names";
const rawName = " MyMap ";
const safeName = ensureProjectFileName(rawName);
// safeName === "MyMap.geolibre.json"
When users provide blank names, the system falls back to DEFAULT_PROJECT_NAME as defined in the same module.
Loading Projects into the Store
Project loading is centralized in the Zustand store at [packages/core/src/store.ts](packages/core/src/store.ts). The loadProject method handles the complete state replacement.
loadProject() Signature and Behavior
loadProject(project: Project, path?: string, options?: LoadOptions): void
Located at useAppStore.loadProject, this method:
- Clears transient UI state (editor overlays, selected layers)
- Populates the store with parsed project data
- Updates the recent-projects list (unless
rememberRecent: false) - Resolves XYZ-based layer URLs when loading from external sources
The function accepts projects from multiple origins: UI file dialogs, URL hash parameters, drag-and-drop events, or the embed bridge API.
Saving Projects: Desktop and Web
GeoLibre abstracts platform differences through [apps/geolibre-desktop/src/lib/tauri-io.ts](apps/geolibre-desktop/src/lib/tauri-io.ts).
Save Methods
| Method | Platform | Use Case |
|---|---|---|
saveProjectFile(content) |
Desktop/Web | Interactive save with native dialog or browser download |
saveProjectFileToPath(content, path) |
Desktop only | Programmatic writes to specific paths |
The implementation at tauri-io.ts#L2947-L2993 handles:
- Desktop: Tauri file dialog with permission handling
- Web: Automatic download trigger with suggested filename
import { saveProjectFileToPath } from "@/lib/tauri-io";
import { useAppStore } from "@geolibre/core";
async function saveCurrentProject() {
const json = JSON.stringify(useAppStore.getState().project);
const path = "/tmp/my-project.geolibre.json";
await saveProjectFileToPath(json, path);
}
Source: saveProjectFileToPath
Project Lifecycle Hooks
The [useProjectFileActions.ts](apps/geolibre-desktop/src/hooks/useProjectFileActions.ts) hook orchestrates the complete project lifecycle:
- New project: Generates empty JSON with UUID via
newProject() - Load project: Parses JSON, resolves layer URLs, injects into store
- Save project: Serializes current store state to JSON
These actions combine file-name sanitization, I/O operations, and state management into cohesive UI workflows.
Loading from URLs and External Sources
The [useProjectUrlLoader.ts](apps/geolibre-desktop/src/hooks/useProjectUrlLoader.ts#L12-L37) hook enables project loading from remote URLs:
import { useProjectUrlLoader } from "@/hooks/useProjectUrlLoader";
function openFromUrl(url: string) {
// Fetches file, resolves XYZ layers, calls loadProject internally
useProjectUrlLoader({ projectUrl: url });
}
Security note: URL validation is enforced through an allow-list in [embed-api.ts](apps/geolibre-desktop/src/lib/embed-api.ts). Only approved domains may load projects via the embed bridge.
Creating Projects Manually for Testing
For development and automated testing, you can construct minimal valid projects:
{
"version": "0.1.0",
"name": "Blank Map",
"mapView": { "center": [0, 0], "zoom": 2, "bearing": 0, "pitch": 0 },
"basemapStyleUrl": "",
"basemapVisible": true,
"basemapOpacity": 1,
"layers": [],
"styles": {}
}
Save as blank.geolibre.json and open via UI or loadProject() to verify round-trip serialization.
Testing the File System
Unit and integration tests demonstrate proper usage:
- [
tests/file-names.test.ts](tests/file-names.test.ts):ensureProjectFileNameedge cases - [
tests/core-project.test.ts](tests/core-project.test.ts): Full load/save lifecycle validation
These tests verify that project state survives serialization without data loss.
Summary
- Single-file architecture: GeoLibre uses
.geolibre.jsonas the authoritative project representation - Platform abstraction:
saveProjectFile()andsaveProjectFileToPath()handle desktop and web I/O uniformly - Centralized loading:
loadProject()inpackages/core/src/store.tsmanages all state replacement - Validation layers:
ensureProjectFileName()and embed API allow-lists enforce security and consistency - Testable design: Pure utilities enable unit testing; integration tests cover full workflows
Frequently Asked Questions
What happens if I rename a .geolibre.json file manually?
The file will load correctly as long as the extension remains .geolibre.json. The name field inside the JSON controls the display name, not the filesystem name. Use ensureProjectFileName() in your code to maintain consistency.
Can I version control GeoLibre projects?
Yes. The .geolibre.json format is plain JSON with deterministic key ordering, making it suitable for Git. The version field enables migration logic when opening files created with older schema versions.
How do I load a project from a URL without adding it to recent projects?
Pass rememberRecent: false in the options parameter: loadProject(project, path, { rememberRecent: false }). This is used by useProjectUrlLoader for embed scenarios where the source URL should not persist in the user's project history.
Is the file format stable across GeoLibre versions?
The schema is versioned via the version field. According to the source in [docs/project-format.md](docs/project-format.md), backward compatibility is maintained where possible, with automatic migration for older formats. Always specify the version when creating files programmatically.
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 →