Skip to content

Upgrading to 0.9

maplibre-yaml 0.9 (@maplibre-yaml/core 0.9, @maplibre-yaml/astro 0.7, @maplibre-yaml/cli 0.6) adds thematic maps, large-data formats such as PMTiles and GeoParquet, and map titles, descriptions and translations. Most maps upgrade with no change to their YAML. This page lists how to upgrade, the changes you might notice, and what is new.

The full list of changes is in each package’s changelog on npm.

A project keeps the version it was set up with: nothing moves to 0.9 until you change it.

The version is part of the script’s address. Use the current release line in the register.js line of your page:

<script
type="module"
src="https://unpkg.com/@maplibre-yaml/core@0.9/register.js"
></script>

The number after core@ names a release line: the address loads the newest release in that line, so bug fixes arrive by themselves, but a new line, which may behave differently, arrives only when you change the number (Versions in CDN URLs). A story page (<ml-scrollytelling>) uses the same script.

If you load the JavaScript API from esm.sh, change the number in that address the same way: https://esm.sh/@maplibre-yaml/core@0.9?external=maplibre-gl.

Your YAML keeps working. These are the changes you might see after upgrading, and what to do about each, if anything.

  • The legend’s header always reads “Legend”. On a plain map, a custom legend.title now appears as a heading inside the panel instead of replacing the header, matching story legends; a screen reader announces every legend the same way. No change to your YAML. CSS that styled .legend-title as your title should target .legend-heading. (Legend)
  • Author hints show only while you develop. The on-page notice keeps showing failures (a data file that didn’t load, a layer MapLibre rejected) everywhere, but hints meant for you (a “did you mean”, a key in the wrong place, coordinates that look written latitude-first) now show only on localhost, in a file:// page and under astro dev or vite, not on your published site. Add the debug attribute (or the debug prop on the Astro components) to see them on a deployed page; the console always has them. (Reading what the map tells you)
  • Strings that start with = in a style value now warn. The format reserves them for a future shorthand: text-field: "=upper({name})" used to draw the text as written and now gets a warning suggesting a MapLibre expression. Text that really starts with = is written ["literal", "=…"]. (Strings starting with = are reserved)
  • A map without a basemap now warns in validation (mlym validate included). <ml-map> already refused such a map with an error card; the warning says so before you load the page. Add mapStyle: under config:.
  • Popup numbers follow the page’s language. A format: ",.0f" number used to be written in US English everywhere; on a page with <html lang="de"> it now reads 8.336.817. Name a locale in the format to fix one regardless of the page. (Number formats)
  • bounds accepts both of MapLibre’s forms, four numbers [west, south, east, north] or two corners [[west, south], [east, north]] (the second used to fail). Values that validated before but broke the map, such as a latitude beyond ±90 or three numbers, are now validation errors with the expected order spelled out. (Fit to bounds)
  • fitTo on a url: source opens already framed on the data, with no jump from center/zoom. While the file loads, the map waits up to 1.5 seconds before its first paint; on a slow file it then draws at center/zoom and frames the data when it arrives, as before. (Fit to a source’s data)
  • fitTo keeps a margin around the data. Without padding:, the data is framed with a margin (8% of the map’s shorter side, 12 to 48 pixels, plus room for the attribution at the bottom), so circles and icons at its edges are drawn whole instead of cut in half. Write padding: 0 for the exact extent, as 0.8 framed it, or any padding: of your own. (Fit to a source’s data)
  • A layer’s own legend entry shows a legend. A layer with legend: { label, color } used to appear only when the document also had a legend: block; now the map shows a legend for it anyway, as it does for a classified layer. Write legend: false at the top level to keep a map without one. (Legend)
  • mlym emit writes a GeoJSON url: source as data: <url>, the way the MapLibre style spec expects, so the exported style.json now loads in MapLibre. A live url: source (refresh:, stream:) is reported as a contract warning instead of lossy. (emit)
  • A slow map says it is loading. If a map hasn’t drawn after about a second, a small “Loading map…” appears in its middle until the basemap draws. A page with its own loading indicator hides it with ml-map .ml-map-loading { display: none; }.
  • A layer whose before: names a layer the map doesn’t have is drawn on top instead of disappearing (MapLibre used to refuse it). While you develop, the author notes suggest the closest layer id.
  • The CDN script is larger: register.browser.js grows from about 125 KB to about 175 KB gzip, for thematic maps, translations, number formatting and the large-data formats. The format readers themselves are separate files, downloaded only by maps that use them.
  • Thematic maps. A layer’s classify: block makes a choropleth, graduated symbols or a category map from your data, with a legend generated for it: explicit breaks or quantile, equal-interval and Jenks classes, named colour palettes, rates with normalize:, and a colour for missing values. See Thematic maps & legends and the thematic maps example.
  • Large data from YAML alone. PMTiles, FlatGeobuf, GeoParquet and Cloud Optimized GeoTIFF (COG) files, read in parts straight from a static host such as GitHub Pages, and a whole basemap from one Protomaps .pmtiles file with no API key (mapStyle: data/city.pmtiles is enough). See Large data: PMTiles, FlatGeobuf, GeoParquet & COG.
  • Accessible, translated maps. A document’s title: and description: name and describe the map for screen readers; locale: translates every piece of text the library puts on the map; popup numbers use Intl.NumberFormat options. See Accessibility & languages.
  • Smaller conveniences:
    • fitTo: tracts, just the source name, as shorthand for fitTo: { source: tracts }.
    • legend: false on a layer keeps it out of the legend.
    • The debug attribute on <ml-map> and <ml-scrollytelling> (and the debug prop on the Astro components) shows author hints on a published page.
    • On a narrow map, a legend and a layer panel in two corners of the same edge no longer overlap, and a story’s pinned legend no longer covers the map’s notice.
    • mlym validate prints notes (ℹ note) that explain how a valid value is read, such as a basemap written as just a .pmtiles file. Notes are not warnings: the file still reads ✓ valid, and --strict never fails on them.
    • mlym init --template basic and --template story load core at the release line the CLI was built with, so a new project gets the features these docs describe.

See Troubleshooting. If a map that worked on 0.8 stops working on 0.9 and the notes above don’t explain it, please open an issue with the YAML and what you see.

Moving a project from an older line, such as 0.5, straight to 0.9? Read the notes for each line you skip, oldest first. Each package’s changelog on npm has the full list.

Three behaviour changes matter when you move to 0.6:

  • !html in popups is trust-gated, not unconditional. A popup !html value renders as raw markup only under a trusted capability policy; otherwise it is escaped and shown as text. <ml-map> is untrusted by default, so there !html content is escaped. In Astro, loadYAML and loadFromGlob now parse with core’s own options, so an !html tag reaches the map as the same { $html } value core produces (before, the Astro loader silently turned it into a plain string); it then renders under the same trust rule.
  • Malformed inline GeoJSON now warns. A v1 document whose inline GeoJSON data is malformed gets a warning, which mlym validate --strict and CI runs treat as an error. Check inline geometry before upgrading.
  • Format v2 style.metadata carries through. In a v2 document, style.metadata is copied to the root metadata of the emitted style.json.

With @maplibre-yaml/core 0.6.0 or earlier, every @maplibre-yaml/astro component fails under astro dev with Failed to create map: Map is not a constructor, while astro build works: Vite never pre-bundles maplibre-gl when only a dependency’s .astro file imports it. Current releases are not affected; upgrade core and the Astro package together (How to upgrade). If a project must stay on core 0.6.0, tell Vite to pre-bundle maplibre-gl:

astro.config.mjs
import { defineConfig } from "astro/config";
export default defineConfig({
vite: { optimizeDeps: { include: ["maplibre-gl"] } },
});