# Developing with the GeoLibre File System: A Complete Guide to `.geolibre.json`

> Learn to develop with the GeoLibre file system. This guide details how .geolibre.json files manage maps, layers, styles, and widgets for portable workspace representation.

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

---

**GeoLibre stores every workspace as a single [`.geolibre.json`](https://github.com/opengeos/GeoLibre/blob/main/.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`](https://github.com/opengeos/GeoLibre/blob/main/.geolibre.json) format and its associated APIs is essential.

## What Is the [`.geolibre.json`](https://github.com/opengeos/GeoLibre/blob/main/.geolibre.json) File Format?

Every GeoLibre project persists to a single JSON file with the [`.geolibre.json`](https://github.com/opengeos/GeoLibre/blob/main/.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`](https://github.com/opengeos/GeoLibre/blob/main/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`](https://github.com/opengeos/GeoLibre/blob/main/.geolibre.json) includes:

- `version`: Schema version (e.g., `"0.1.0"`)
- `name`: Human-readable project name
- `mapView`: Center, zoom, bearing, and pitch
- `basemapStyleUrl`, `basemapVisible`, `basemapOpacity`: Basemap configuration
- `layers`: Array of layer objects with source and style references
- `styles`: 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`](https://github.com/opengeos/GeoLibre/blob/main/apps/geolibre-desktop/src/lib/file-names.ts)](apps/geolibre-desktop/src/lib/file-names.ts).

### `ensureProjectFileName()`

The [`ensureProjectFileName`](apps/geolibre-desktop/src/lib/file-names.ts#L15-L19) function guarantees valid project file names by:

- Trimming whitespace from user input
- Appending [`.geolibre.json`](https://github.com/opengeos/GeoLibre/blob/main/.geolibre.json) if the extension is missing
- Preserving existing extensions when already present

```typescript
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`](https://github.com/opengeos/GeoLibre/blob/main/packages/core/src/store.ts)](packages/core/src/store.ts). The `loadProject` method handles the complete state replacement.

### `loadProject()` Signature and Behavior

```typescript
loadProject(project: Project, path?: string, options?: LoadOptions): void

```

Located at [`useAppStore.loadProject`](packages/core/src/store.ts#L541-L565), this method:

1. Clears transient UI state (editor overlays, selected layers)
2. Populates the store with parsed project data
3. Updates the recent-projects list (unless `rememberRecent: false`)
4. 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`](https://github.com/opengeos/GeoLibre/blob/main/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`](apps/geolibre-desktop/src/lib/tauri-io.ts#L2947-L2993) handles:

- Desktop: Tauri file dialog with permission handling
- Web: Automatic download trigger with suggested filename

```typescript
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`](apps/geolibre-desktop/src/lib/tauri-io.ts#L2984-L2988)

## Project Lifecycle Hooks

The [[`useProjectFileActions.ts`](https://github.com/opengeos/GeoLibre/blob/main/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`](https://github.com/opengeos/GeoLibre/blob/main/useProjectUrlLoader.ts)](apps/geolibre-desktop/src/hooks/useProjectUrlLoader.ts#L12-L37) hook enables project loading from remote URLs:

```typescript
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`](https://github.com/opengeos/GeoLibre/blob/main/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:

```json
{
  "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`](https://github.com/opengeos/GeoLibre/blob/main/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`](https://github.com/opengeos/GeoLibre/blob/main/tests/file-names.test.ts)](tests/file-names.test.ts): `ensureProjectFileName` edge cases
- [[`tests/core-project.test.ts`](https://github.com/opengeos/GeoLibre/blob/main/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.json`](https://github.com/opengeos/GeoLibre/blob/main/.geolibre.json) as the authoritative project representation
- **Platform abstraction**: `saveProjectFile()` and `saveProjectFileToPath()` handle desktop and web I/O uniformly
- **Centralized loading**: `loadProject()` in [`packages/core/src/store.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/core/src/store.ts) manages 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`](https://github.com/opengeos/GeoLibre/blob/main/.geolibre.json) file manually?

The file will load correctly as long as the extension remains [`.geolibre.json`](https://github.com/opengeos/GeoLibre/blob/main/.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`](https://github.com/opengeos/GeoLibre/blob/main/.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`](https://github.com/opengeos/GeoLibre/blob/main/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.