Skip to main content
Login
Sign up

Map

The built-in map tag — an embedded OpenFreeMap / MapLibre map with one pin per location. Pins take a street address (geocoded at build time, no API key) or explicit coordinates. Usage, options, and a live example.

A built-in tag that embeds an interactive map with a marker for each location. It renders OpenFreeMap vector tiles with MapLibre GL JS — both free and keyless — so a map just works with nothing to sign up for.

Drop a pin by street address and the address is turned into coordinates once, at build time, by the free Nominatim (OpenStreetMap) geocoder — cached so a rebuild never re-looks-up an address it already knows. Prefer to be exact (or build offline)? Give a pin explicit lat / lng and no geocoding happens at all.

Usage

Wrap the map in {% map %} … {% endMap %} and give each location its own self-closing {% pin %}. The simplest form is an address and a label:

{% map %}
{% pin address="British Museum, London" label="British Museum" %}
{% pin address="Tower of London, London" label="Tower of London" %}
{% pin address="Buckingham Palace, London" label="Buckingham Palace" %}
{% endMap %}

At build time each address is geocoded and baked into the page as coordinates, so the reader’s browser only loads the basemap and drops the markers — it never geocodes.

Live example

The same three London landmarks, pinned by explicit coordinates so the page needs no network when it builds — the source below and the map it renders match exactly. With no center or zoom set, the map frames all the pins automatically:

{% map height=420 %}
{% pin lat=51.5194 lng=-0.1270 label="British Museum" description="Great Russell St" %}
{% pin lat=51.5081 lng=-0.0759 label="Tower of London" description="A royal fortress since 1066" %}
{% pin lat=51.5014 lng=-0.1419 label="Buckingham Palace" color="#c2255c" %}
{% endMap %}

renders, live:

Click a marker for its popup. The map keeps the © OpenStreetMap contributors and OpenFreeMap attribution in the corner — that crediting is required, so don’t remove it.

Pinning a location

A {% pin %} is placed one of two ways:

  • By address{% pin address="350 Fifth Ave, New York, NY" %}. Geocoded at build time (cached). Easiest to author; needs a network connection the first time it’s built.
  • By coordinates{% pin lat=40.7484 lng=-73.9857 %}. Exact, reproducible, and never touches the network. Use this for precision, for places a geocoder won’t find, or to keep a build fully offline.

An address that can’t be resolved (typo, or an offline build with a cold cache) is skipped with a build warning rather than failing the build — so one bad address never breaks your docs.

Options

Every {% map %} attribute is optional:

{% map %} attribute Effect
style="liberty" Basemap style: liberty (default), bright, positron, or a full MapLibre style URL.
zoom=12 Initial zoom level. Omit to auto-fit the pins.
center="51.50,-0.12" Center as "lat,lng" — or an address. Omit to auto-fit the pins.
height=420 Map height in pixels (default 400).
interactive=false Lock the map (no pan/zoom, no zoom buttons) for a static locator.

Each {% pin %} takes:

{% pin %} attribute Effect
address="…" Street address, geocoded at build time. Use this or lat/lng.
lat=… lng=… Explicit coordinates (skips geocoding). lon is accepted as an alias for lng.
label="…" Bold heading in the marker’s popup (and the location’s name in the fallback list).
description="…" A line of detail below the label in the popup.
color="#c2255c" Marker color (any CSS color).

Geocoding, privacy & offline builds

Geocoding runs only at build time and only for address pins — the published page contains coordinates, never an address lookup. Results are cached under .aardvark-cache/ (git-ignored), so addresses are resolved once and reused on every later build.

Forcing a re-lookup

Each geocoded address is cached as one small JSON file under .aardvark-cache/geo/, named by a hash of the address (its contents are the resolved lat / lng and place name). The cache is purely derived and git-ignored, so clearing it is always safe — it just costs a fresh lookup on the next build. To drop every cached coordinate and re-resolve on the next build:

rm -rf .aardvark-cache/geo

To drop a single entry — say the geocoder placed a pin in the wrong spot and you want it looked up again — the filenames are hashed, so match the file by the (wrong) coordinate you saw on the map, then delete it and rebuild:

grep -rl 43.6532 .aardvark-cache/geo | xargs rm

(To skip geocoding for a location altogether, give its pin explicit lat / lng.)

The default geocoder is Nominatim’s public server, used within its usage policy: one request per second, a descriptive User-Agent, and aggressive caching. For heavier use, switch to Google — all via the optional map block in aardvark.config.yaml:

map:
  geocoder: nominatim          # default; or "google"
  # googleApiKey: "…"          # required when geocoder: google
  # rateLimit: 1.0             # requests per second
  # timeout: 10                # seconds per geocoder request
  style: liberty               # default basemap
  # maplibreVersion: "5.24.0"  # pin a different MapLibre release

MapLibre GL JS loads from a pinned CDN release whose Subresource Integrity hash the browser verifies. If you override maplibreVersion (or point maplibreJs / maplibreCss at your own URLs), aardvark can’t know the hash and ships no SRI for it — you then own the supply-chain integrity of whatever that release serves.

Content-Security-Policy: SRI checks the bytes but doesn’t grant permission to fetch them. If your site sends a CSP, the browser blocks MapLibre’s <script> and <link> unless their origin is allow-listed — and the map silently drops to the fallback list below with a console error. The default release loads from cdn.jsdelivr.net, so add https://cdn.jsdelivr.net to both script-src and style-src, or point maplibreJs / maplibreCss at a self-hosted copy so no third-party origin is needed. (Cloud hosts and WAFs sometimes set a CSP for you — check there if a map renders fine locally but vanishes once deployed.)

No JavaScript? A reader (or a search crawler) without the map still gets an accessible list of the pinned locations, each linking to its spot on OpenStreetMap — the same list screen readers use.

Last modified

Was this page helpful?