Protomaps Basemaps

Generate MapLibre GL style specs and cartographic layers for Protomaps PMTiles vector tiles.

Library
npm
v5.7.2
715stars
BSD 3-Clause License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
67/100Good
Development Activity80
Maintenance36
Community64
Maturity60
Momentum28

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
74/100Good
Architecture76
Code Quality78
Innovation72
Learning Curve68

Protomaps Basemaps is the TypeScript styling layer of the Protomaps basemap project: it turns a PMTiles-backed vector tile source into a ready-to-render MapLibre GL style specification. Rather than hand-writing hundreds of layer definitions, developers call a small set of exported functions to assemble the full basemap layer stack — earth, landcover, water, roads, buildings, boundaries, and multilingual labels — pre-tuned for OpenStreetMap-derived data.

The package ships five built-in color flavors (light, dark, white, grayscale, black) plus a language module that resolves the correct script and font per locale so labels render legibly across writing systems. A companion CLI (generate_style) wraps the same logic to emit a static style.json file for build pipelines that don’t want a runtime dependency.

What You Get

  • A layers(source, flavor, options) function that returns the complete MapLibre GL layer stack (background, earth, landcover, water, buildings, roads, boundaries, labels) for a given vector tile source name
  • Five built-in Flavor presets — LIGHT, DARK, WHITE, GRAYSCALE, BLACK — each a plain object of ~90 named colors that can be spread and partially overridden for custom themes
  • A language module (get_multiline_name, get_country_name, language_script_pairs) that selects the correct name field and font per script so labels render correctly across Latin, CJK, Devanagari, Arabic, and other scripts
  • A generate_style CLI and generateStyle() function that assemble a full StyleSpecification (sources, glyphs, sprite, layers) from a TileJSON URL, flavor, and language, for use in static build pipelines
  • TypeScript types (Flavor, Pois, Landcover) so custom themes and partial overrides are checked at compile time

Common Use Cases

  • Rendering a full-featured, multilingual basemap in a MapLibre GL JS or MapLibre Native app backed by Protomaps PMTiles, without writing raw style JSON
  • Generating a static style.json at build time (via the generate_style CLI) for CDN-hosted or offline map deployments
  • Building a custom-branded map theme by spreading a built-in flavor (e.g. DARK) and overriding a handful of colors while keeping the full layer stack intact
  • Swapping label language at runtime (e.g. en, ja, ar) using the same layer stack, for internationalized map products

Under The Hood

Architecture — The package is organized as four cooperating modules under styles/src/: flavors.ts defines the Flavor interface and the five built-in color presets as flat objects of ~90 named color slots; base_layers.ts (the largest file, ~2,070 lines) exports nolabels_layers() and labels_layers(), which map a Flavor plus a vector-tile source name into an ordered array of MapLibre GL LayerSpecification objects (background through bridges, then labels); language.ts resolves per-script name fields and font stacks for label layers; and index.ts composes these into the single public layers(source, flavor, options) entry point plus namedFlavor(). generate_style.ts and cli.ts sit on top as a thin CLI that reads a TileJSON URL and flavor argument and writes a full StyleSpecification (sources, glyphs, sprite, layers) to disk — no network calls happen inside the library itself; it only assembles JSON describing how a MapLibre renderer should draw tiles it fetches separately.

Tech Stack — Written entirely in TypeScript, built with tsup to dual ESM/CJS output (dist/esm, dist/cjs) with generated .d.ts types, and run/tested via tsx’s native Node test runner rather than a separate test framework. The only runtime-relevant dependency is @maplibre/maplibre-gl-style-spec, used both for its LayerSpecification/StyleSpecification types and, in tests, its validateStyleMin() validator. Linting/formatting is handled by Biome instead of ESLint/Prettier. The package lives inside a larger monorepo (protomaps/basemaps) alongside a Java-based Planetiler tile-generation profile and a SvelteKit-based demo app, but the npm package itself has no dependency on either.

Code Quality — Test coverage is real but narrow: five test files under styles/test/ validate that every built-in flavor produces a spec-valid style (via validateStyleMin), that custom flavor overrides (spreading DARK with one color changed) still validate, and that the CLI’s generateStyle() produces expected output — but there is no per-layer unit testing of the ~2,000-line base_layers.ts beyond end-to-end style validation. Naming is consistent and descriptive throughout (nolabels_layers, get_multiline_name), TypeScript types are used pervasively including for the large Flavor interface, and the code has almost no inline comments, relying on type signatures and small function bodies for readability.

API Design — The public surface is intentionally minimal: two functions (layers, namedFlavor) and five exported constants cover the common path, and the Flavor object’s plain-data shape makes theming a matter of object spreading rather than a fluent builder API, which is low-boilerplate but requires consulting flavors.ts to know all ~90 color keys since documentation is limited to a one-paragraph README plus generated TypeDoc. There is no runtime validation of custom Flavor objects — malformed themes are only caught by TypeScript at compile time or by manually running the style through validateStyleMin.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search