Astro content collections
This page shows how to put maps on the pages of an Astro content collection, using content collection schemas and map builder utilities. Whether you’re creating a travel blog, restaurant reviews, or hiking guides, maplibre-yaml provides simple patterns for adding geographic context.
Overview
Section titled “Overview”There are two ways to use maps with a content collection:
- Geographic data in your entries. A blog post, project page or trail guide carries a location, a region, a route, or a reference to a feature in a GeoJSON file in its frontmatter, and its page builds a map from it. Most of this page is about this way.
- Whole map documents as entries. Each entry is a map (or a story) YAML file, validated at build time. See Map documents as collection entries.
The first way uses three key concepts:
- Content Collection Schemas - Pre-built Zod schemas that add location/region/route fields to your collection item frontmatter
- Map Builder Utilities - Functions that convert simple geographic data into full map configurations
- Global Configuration - Site-wide map style settings that individual posts can inherit
Quick Start
Section titled “Quick Start”1. Define Your Collection Schema
Section titled “1. Define Your Collection Schema”Choose the schema that matches your content type:
import { defineCollection } from "astro:content";import { glob } from "astro/loaders";import { getCollectionItemWithLocationSchema } from "@maplibre-yaml/astro/utils";
export const collections = { posts: defineCollection({ loader: glob({ pattern: "**/*.md", base: "./src/collections/posts" }), schema: getCollectionItemWithLocationSchema(), }),};A glob() loader works on Astro 5, 6 and 7 as written. On Astro 4 it needs
experimental: { contentLayer: true }, the config file at
src/content/config.ts, and a base outside src/content/ (see
Supported versions). Loader entries
have an id (the file name without its extension) and render with render()
from astro:content; the legacy type: "content" API (post.slug,
post.render()) is gone in Astro 6.
2. Add Location to Your Posts
Section titled “2. Add Location to Your Posts”---title: "My Trip to Paris"pubDate: 2024-03-15location: coordinates: [2.3522, 48.8566] name: "Paris, France" zoom: 12---We arrived in Paris on a crisp spring morning...3. Render the Map
Section titled “3. Render the Map”---import { getCollection, render } from 'astro:content';import { Map, buildPointMapConfig } from '@maplibre-yaml/astro';
export async function getStaticPaths() { const posts = await getCollection('posts'); return posts.map(post => ({ params: { slug: post.id }, props: { post } }));}
const { post } = Astro.props;const { Content } = await render(post);
// Build map config if location existsconst mapConfig = post.data.location ? buildPointMapConfig({ location: post.data.location, mapStyle: 'https://tiles.openfreemap.org/styles/liberty' }) : null;---
<html lang="en"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width" /> <title>{post.data.title}</title> </head> <body> <article> <h1>{post.data.title}</h1>
{mapConfig && ( <Map config={mapConfig} height="300px" /> )}
<Content /> </article> </body></html>Schema Options
Section titled “Schema Options”Single Location Posts
Section titled “Single Location Posts”Best for: Restaurant reviews, place spotlights, single-destination content.
import { getCollectionItemWithLocationSchema } from "@maplibre-yaml/astro/utils";
const schema = getCollectionItemWithLocationSchema();Frontmatter example:
---title: "Best Coffee in Brooklyn"pubDate: 2024-03-15location: coordinates: [-73.9857, 40.6892] name: "Devoción Coffee" zoom: 16 markerColor: "#8B4513"---Multiple Locations Posts
Section titled “Multiple Locations Posts”Best for: Travel itineraries, city guides, comparison posts.
import { getCollectionItemWithLocationsSchema } from "@maplibre-yaml/astro/utils";
const schema = getCollectionItemWithLocationsSchema();Frontmatter example:
---title: "10 Days in Japan"pubDate: 2024-03-15locations: - coordinates: [139.6917, 35.6895] name: "Tokyo" description: "Days 1-4" - coordinates: [135.7681, 35.0116] name: "Kyoto" description: "Days 5-7" - coordinates: [135.5023, 34.6937] name: "Osaka" description: "Days 8-10"---Region/Area Posts
Section titled “Region/Area Posts”Best for: Neighborhood guides, property coverage areas, territorial content.
import { getCollectionItemWithRegionSchema } from "@maplibre-yaml/astro/utils";
const schema = getCollectionItemWithRegionSchema();Frontmatter example:
---title: "Guide to Brooklyn Heights"pubDate: 2024-03-15region: name: "Brooklyn Heights" coordinates: - [ [-73.998, 40.698], [-73.985, 40.698], [-73.985, 40.692], [-73.998, 40.692], [-73.998, 40.698], ] fillColor: "#3388ff" fillOpacity: 0.3---Route/Trail Posts
Section titled “Route/Trail Posts”Best for: Hiking guides, road trips, cycling routes.
import { getCollectionItemWithRouteSchema } from "@maplibre-yaml/astro/utils";
const schema = getCollectionItemWithRouteSchema();Frontmatter example:
---title: "Pacific Coast Highway Road Trip"pubDate: 2024-03-15route: name: "SF to LA via PCH" coordinates: - [-122.4194, 37.7749] # San Francisco - [-122.0322, 36.9741] # Santa Cruz - [-121.8947, 36.6002] # Monterey - [-120.6596, 35.2828] # San Luis Obispo - [-119.6982, 34.4208] # Santa Barbara - [-118.2437, 34.0522] # Los Angeles color: "#e74c3c" width: 4---Flexible Schema
Section titled “Flexible Schema”For collection items with mixed content types, use the comprehensive schema:
import { z } from "astro/zod";import { getCollectionItemWithGeoSchema } from "@maplibre-yaml/astro/utils";
const schema = getCollectionItemWithGeoSchema({ // Add your custom fields author: z.string(), category: z.enum(["travel", "hiking", "food", "city-guide"]), tags: z.array(z.string()).default([]),});Adding Geographic Data to Content Collections
Section titled “Adding Geographic Data to Content Collections”You can add map support to any existing Astro content collection by importing the geographic schemas. This works for points, polygons, and lines — all optional, so items without geographic data still work. The getCollectionItemWith*Schema helpers above build a whole schema for you; when your collection already has one, add the geographic fields to it instead.
Schema setup
Section titled “Schema setup”import { defineCollection } from "astro:content";import { z } from "astro/zod";import { glob } from "astro/loaders";import { LocationPointSchema, RegionPolygonSchema, RouteLineSchema,} from "@maplibre-yaml/astro/utils"; // /utils in the content config: see the caution above
const projects = defineCollection({ loader: glob({ pattern: "**/*.md", base: "./src/collections/projects" }), schema: z.object({ title: z.string(), status: z.string(), summary: z.string().optional(), // Geographic data — all optional location: LocationPointSchema.optional(), // point region: RegionPolygonSchema.optional(), // polygon route: RouteLineSchema.optional(), // line }),});
export const collections = { projects };Frontmatter examples
Section titled “Frontmatter examples”The fields take the same frontmatter as in Schema Options:
location as in Single Location Posts, region as
in Region/Area Posts, route as in
Route/Trail Posts, and locations (a list of points) as
in Multiple Locations Posts.
Dynamic map per collection item
Section titled “Dynamic map per collection item”Use buildMapConfigFromEntry in your dynamic route page ([slug].astro). The helper inspects the entry data, dispatches to the appropriate builder by geometry type, and falls back to a supplied default if no geometry is set.
---import { getCollection } from "astro:content";import { Map, buildMapConfigFromEntry } from "@maplibre-yaml/astro";import { globalMapConfig } from "../../lib/map-config";
export async function getStaticPaths() { const entries = await getCollection("projects"); return entries.map((entry) => ({ params: { slug: entry.id }, props: { entry } }));}
const { entry } = Astro.props;const mapConfig = await buildMapConfigFromEntry(entry.data, globalMapConfig, { label: entry.data.title, description: entry.data.summary, fallback: { coordinates: globalMapConfig.defaultCenter!, name: entry.data.title, },});---<Map config={mapConfig} height="300px" />globalMapConfig here is the site-wide configuration loaded with
loadGlobalMapConfig (Global Configuration),
which supplies the basemap and the defaultCenter used as the fallback.
The helper recognizes these fields in this precedence order:
feature_ref— external GeoJSON file referenceregion— inline polygonroute— inline linelocations— inline multi-pointlocation— inline single pointoptions.fallback— supplied default
The first set field wins; later fields are ignored. The helper is always async because feature_ref requires file I/O at build time.
To handle each geometry type yourself (custom popups or styling per type), call the builders directly instead, as the Complete Blog Template Example below does.
Map Builder Functions
Section titled “Map Builder Functions”buildPointMapConfig
Section titled “buildPointMapConfig”Creates a map centered on a single location with a marker:
import { buildPointMapConfig } from "@maplibre-yaml/astro";
const config = buildPointMapConfig({ location: { coordinates: [-74.006, 40.7128], name: "New York City", description: "The Big Apple", }, mapStyle: "https://tiles.openfreemap.org/styles/liberty", zoom: 12, // optional, defaults to location.zoom or 12 interactive: true, // optional, defaults to true});buildMultiPointMapConfig
Section titled “buildMultiPointMapConfig”Creates a map showing multiple locations with automatic bounds fitting:
import { buildMultiPointMapConfig } from "@maplibre-yaml/astro";
const config = buildMultiPointMapConfig({ locations: [ { coordinates: [-74.006, 40.7128], name: "NYC" }, { coordinates: [-118.2437, 34.0522], name: "LA" }, { coordinates: [-87.6298, 41.8781], name: "Chicago" }, ], mapStyle: "https://tiles.openfreemap.org/styles/liberty",});buildMultiPointMapConfig and buildRouteMapConfig also take padding: the
pixels kept between the data and the map edge when the map fits it (default
50; applied from astro 0.7.1).
buildPolygonMapConfig
Section titled “buildPolygonMapConfig”Creates a map displaying a polygon region:
import { buildPolygonMapConfig } from "@maplibre-yaml/astro";
const config = buildPolygonMapConfig({ region: { coordinates: [ [ [-74.0, 40.7], [-73.9, 40.7], [-73.9, 40.8], [-74.0, 40.8], [-74.0, 40.7], // Close the polygon ], ], name: "Manhattan", fillColor: "#ff6b6b", fillOpacity: 0.4, }, mapStyle: "https://tiles.openfreemap.org/styles/liberty",});buildRouteMapConfig
Section titled “buildRouteMapConfig”Creates a map showing a line/route with start/end markers:
import { buildRouteMapConfig } from "@maplibre-yaml/astro";
const config = buildRouteMapConfig({ route: { coordinates: [ [-122.4194, 37.7749], [-121.8947, 36.6002], [-118.2437, 34.0522], ], name: "California Road Trip", color: "#e74c3c", width: 4, }, mapStyle: "https://tiles.openfreemap.org/styles/liberty",});Other builders
Section titled “Other builders”buildMultiPolygonMapConfig and buildMultiLineStringMapConfig take several
polygons or lines, and buildFeatureMapConfig builds a map from a
GeoJSON feature reference.
buildMapConfigFromEntry picks the right builder from an entry’s fields
(Dynamic map per collection item). Every
builder takes a global configuration as its
second argument.
Global Configuration Pattern
Section titled “Global Configuration Pattern”Instead of specifying mapStyle on every map, create a shared configuration.
For a validated, YAML-backed version that every builder accepts as its
second argument, see loadGlobalMapConfig in
Astro: Global Configuration.
A plain object works too:
export const globalMapConfig = { mapStyle: "https://tiles.openfreemap.org/styles/liberty", // Add any other defaults you want to share};mapStyle takes a MapLibre style URL or, for a basemap you host yourself
with no API key, a Protomaps .pmtiles file in public/:
{ pmtiles: "data/city.pmtiles", flavor: "light" }
(Host your own basemap).
Then use it in your pages:
---import { Map, buildPointMapConfig } from '@maplibre-yaml/astro';import { globalMapConfig } from '../lib/map-config';
const { location } = Astro.props.post.data;
const mapConfig = location ? buildPointMapConfig({ location, mapStyle: globalMapConfig.mapStyle }) : null;---
{mapConfig && <Map config={mapConfig} height="300px" />}Complete Blog Template Example
Section titled “Complete Blog Template Example”Here’s a full example of a collection item post template with conditional map rendering:
---import { getCollection, render } from 'astro:content';import { Map, buildPointMapConfig, buildMultiPointMapConfig, buildPolygonMapConfig, buildRouteMapConfig} from '@maplibre-yaml/astro';
export async function getStaticPaths() { const posts = await getCollection('posts'); return posts.map(post => ({ params: { slug: post.id }, props: { post } }));}
const { post } = Astro.props;const { Content } = await render(post);const { location, locations, region, route } = post.data;
const MAP_STYLE = 'https://tiles.openfreemap.org/styles/liberty';
// Build appropriate map config based on available datafunction getMapConfig() { if (location) { return buildPointMapConfig({ location, mapStyle: MAP_STYLE }); } if (locations && locations.length > 0) { return buildMultiPointMapConfig({ locations, mapStyle: MAP_STYLE }); } if (region) { return buildPolygonMapConfig({ region, mapStyle: MAP_STYLE }); } if (route) { return buildRouteMapConfig({ route, mapStyle: MAP_STYLE }); } return null;}
const mapConfig = getMapConfig();---
<html lang="en"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width" /> <title>{post.data.title}</title> </head> <body> <article class="post"> <header> <h1>{post.data.title}</h1> <time datetime={post.data.pubDate.toISOString()}> {post.data.pubDate.toLocaleDateString()} </time> </header>
{mapConfig && ( <div class="post-map"> <Map config={mapConfig} height="350px" /> </div> )}
<div class="post-content"> <Content /> </div> </article> </body></html>
<style> .post-map { margin: 2rem 0; border-radius: 8px; overflow: hidden; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); }</style>GeoJSON Feature References
Section titled “GeoJSON Feature References”When multiple collection items share geometry from a single source-of-truth GeoJSON file, copying coordinates into each frontmatter is fragile and verbose. The feature_ref field lets a collection item point at a feature in an external GeoJSON file (matched by featureId or by property equality). At build time, the loader reads the file, finds the matching feature, detects its geometry type, and dispatches to the existing point/polygon/route builders.
Setting up a feature reference
Section titled “Setting up a feature reference”-
Add
feature_refto your collection schemasrc/content.config.ts import { defineCollection } from "astro:content";import { z } from "astro/zod";import { glob } from "astro/loaders";import {FeatureRefSchema,LocationPointSchema,RegionPolygonSchema,RouteLineSchema,} from "@maplibre-yaml/astro/utils";export const collections = {poas: defineCollection({loader: glob({ pattern: "**/*.md", base: "./src/collections/poas" }),schema: z.object({title: z.string(),gotf_id: z.number(),// Either feature_ref OR inline geometry -- not bothfeature_ref: FeatureRefSchema.optional(),location: LocationPointSchema.optional(),region: RegionPolygonSchema.optional(),route: RouteLineSchema.optional(),}),}),};For automatic mutual-exclusivity validation, use
getCollectionItemWithFeatureRefSchema()instead. It rejects items declaring bothfeature_refand inline geometry at content-collection-parse time. -
Reference features from frontmatter
---title: "Gowanus Rezoning Area"gotf_id: 1.1feature_ref:source: "./src/data/gowanus.geojson"featureId: "poa-1.1"------title: "Gowanus Rezoning Area"gotf_id: 1.1feature_ref:source: "./src/data/gowanus.geojson"match:property: "gotf_id"equals: 1.1fillColor: "#3388ff"fillOpacity: 0.25--- -
Build the map in your dynamic page
The simplest path is
buildMapConfigFromEntry— it inspects the entry data, dispatches by geometry type (includingfeature_ref), and produces aMapBlock:src/pages/poas/[slug].astro ---import { getCollection } from "astro:content";import { Map, buildMapConfigFromEntry } from "@maplibre-yaml/astro";import { globalMapConfig } from "../../lib/map-config";export async function getStaticPaths() {const entries = await getCollection("poas");return entries.map((entry) => ({ params: { slug: entry.id }, props: { entry } }));}const { entry } = Astro.props;const mapConfig = await buildMapConfigFromEntry(entry.data, globalMapConfig, {label: entry.data.title,fallback: { coordinates: globalMapConfig.defaultCenter! },});---<Map config={mapConfig} height="300px" />If you need custom per-type behavior, call the underlying builders directly (
buildFeatureMapConfig,buildPolygonMapConfig, etc.) instead. See Map Builder Functions.
Match strategies
Section titled “Match strategies”Two ways to identify which feature to use:
| Form | When to use |
|---|---|
featureId: <string | number> | When your GeoJSON features have stable top-level id fields (RFC 7946) |
match: { property, equals } | When IDs live in feature.properties (common — mirrors MapLibre’s promoteId) |
Exactly one match must be returned. Zero matches or multiple matches throw GeoJSONLoadError with details (file path, total feature count, sample IDs/values).
Override fields
Section titled “Override fields”Frontmatter can override feature.properties.name and feature.properties.description, plus all style fields (markerColor, fillColor, strokeColor, fillOpacity, color, width, zoom). Overrides take priority; feature.properties are the fallback.
Supported geometry types
Section titled “Supported geometry types”| Type | Builder dispatched |
|---|---|
Point | buildPointMapConfig |
MultiPoint | buildMultiPointMapConfig |
LineString | buildRouteMapConfig |
MultiLineString | buildMultiLineStringMapConfig (renders all segments) |
Polygon | buildPolygonMapConfig |
MultiPolygon | buildMultiPolygonMapConfig (renders all polygons) |
GeometryCollection (single member) | dispatches to the inner geometry type |
GeometryCollection (multiple members) | throws GeoJSONLoadError — split into separate features |
Path resolution and security defaults
Section titled “Path resolution and security defaults”The source value is resolved against process.cwd() by default. Two settings on FeatureLoadOptions adjust this:
import { buildFeatureMapConfig, type FeatureLoadOptions,} from "@maplibre-yaml/astro";
const loadOptions: FeatureLoadOptions = { // Use a custom project root for monorepo builds where cwd != Astro project dir. // Canonicalized via realpath so macOS `/var` -> `/private/var` doesn't trip // the containment check. projectRoot: "/absolute/path/to/astro/project",
// Opt in to absolute `source` paths. Default: false. // Required for tests using tmpdir, monorepo data outside the project root, // or any controlled call site. Do NOT enable when frontmatter values can // come from untrusted content (CMS-supplied entries, user uploads). allowAbsolutePaths: true,};
await buildFeatureMapConfig({ ref, loadOptions }, globalConfig);The same loadOptions flows through buildMapConfigFromEntry and loadFeatureFile.
Error classes
Section titled “Error classes”The builder throws one of two error classes:
import { buildFeatureMapConfig, GeoJSONLoadError, InvalidFeatureRefError,} from "@maplibre-yaml/astro";
try { await buildFeatureMapConfig({ ref }, globalConfig);} catch (err) { if (err instanceof InvalidFeatureRefError) { // Thrown when the ref has neither or both of `featureId` and `match`. // Discriminable via instanceof (not wrapped by buildFeatureMapConfig). } else if (err instanceof GeoJSONLoadError) { // Thrown for everything else: missing file/feature, malformed JSON, // unsupported geometry, path-traversal violation, absolute-path // rejection, file too large, degenerate LineString, heterogeneous // GeometryCollection. `.message` carries the actionable message; // `.filePath` carries the resolved path; ES2022 `.cause` carries the // underlying fs/parse error when applicable. }}Build-time only
Section titled “Build-time only”buildFeatureMapConfig reads from the local filesystem via process.cwd() (or the configured projectRoot). It is build-time only — invoking it in a deployed SSR adapter context throws a GeoJSONLoadError whose ENOENT message includes deployment-context hints. With output: 'server', prerender the pages that use it (see Server output).
Performance
Section titled “Performance”| File size | Behavior |
|---|---|
| < 10MB | Fully supported, no caveats |
| 10-50MB | Supported; one-time parse cost |
| 50-100MB | Build-time warning |
| > 100MB | Hard error directing toward PMTiles or splitting |
For data this large, load it in the browser instead of at build time: a
PMTiles, FlatGeobuf or GeoParquet file in public/ is read in parts as the
map needs it (Large data).
Files referenced multiple times across collection items are parsed once via an mtime-aware cache. Editing the GeoJSON in astro dev invalidates the cache on next page render — no server restart needed. For files with 200+ features, a per-property index is built lazily on the second access for that property.
Ignored style overrides
Section titled “Ignored style overrides”FeatureRef accepts every style field (markerColor, fillColor, strokeColor, fillOpacity, color, width) because the geometry type is not known when the schema parses — it’s resolved when the file loads. When a field doesn’t apply to the resolved geometry (e.g., markerColor on a Polygon ref), the builder ignores it and emits a one-time console.warn during astro dev:
feature_ref: style override(s) [markerColor, width] do not apply to Polygon geometry from "./src/data/sample.geojson"; they will be ignored.Warnings are deduplicated per (source, geometryType, ignored-keys) so reloading the page won’t re-emit them. Restarting astro dev clears the dedupe cache.
Lower-level primitives
Section titled “Lower-level primitives”For advanced cases (e.g., loading one file once and dispatching to many features manually):
import { loadFeatureFile, findFeature, GeoJSONLoadError, clearFeatureCache,} from "@maplibre-yaml/astro";
const fc = await loadFeatureFile("./src/data/gowanus.geojson");const feature = findFeature(fc, { source: "...", featureId: "poa-1.1" });// Then dispatch manually with the existing sync buildersclearFeatureCache() is exported for test isolation between files that share absolute paths.
Utility Functions
Section titled “Utility Functions”calculateCenter
Section titled “calculateCenter”Calculate the center point of multiple coordinates:
import { calculateCenter } from "@maplibre-yaml/astro";
const center = calculateCenter([ [-74.006, 40.7128], [-73.935, 40.73], [-73.867, 40.752],]);// Returns approximately [-73.936, 40.732]calculateBounds
Section titled “calculateBounds”Calculate the bounding box of coordinates:
import { calculateBounds } from "@maplibre-yaml/astro";
const bounds = calculateBounds([ [-74.006, 40.7128], [-73.867, 40.752],]);// Returns [[-74.006, 40.7128], [-73.867, 40.752]]Map documents as collection entries
Section titled “Map documents as collection entries”For a site with many maps that should be validated at build time, keep each
map’s YAML file in a collection. A mistake in any file then fails
astro build (and shows in astro dev) instead of appearing as an error
card in the browser. For a handful of maps, YAML files in public/ loaded
with <Map src> are simpler (Astro).
1. Define the collection in src/content.config.ts:
import { defineCollection } from "astro:content";import { glob } from "astro/loaders";import { getMapSchema } from "@maplibre-yaml/astro/utils";
const maps = defineCollection({ loader: glob({ pattern: "**/*.yaml", base: "./src/collections/maps" }), // The whole map document schema: sources, layers, legend, markers, // parameters, images, every config key. Add fields with extendSchema(). schema: getMapSchema(),});
export const collections = { maps };2. Add YAML files to the collection, one map document each:
type: mapid: headquartersconfig: center: [-122.4194, 37.7749] zoom: 15 maxZoom: 18 mapStyle: "https://tiles.openfreemap.org/styles/liberty"sources: office: type: geojson data: type: Feature geometry: type: Point coordinates: [-122.4194, 37.7749] properties: name: "Headquarters"layers: - id: office type: circle source: office legend: { label: Headquarters, color: "#3b82f6" } paint: circle-radius: 12 circle-color: "#3b82f6"legend: position: bottom-leftmarkers: - at: [-122.4180, 37.7762] color: "#e63946" popup: - p: - str: "Visitor parking"3. Show an entry with <Map>:
---import { getEntry } from 'astro:content';import { Map } from '@maplibre-yaml/astro';
const headquarters = await getEntry('maps', 'headquarters');if (!headquarters) throw new Error('maps/headquarters.yaml not found');---
<Map config={headquarters.data} height="500px" />To render an entry with your own component around <ml-map> instead, pass
JSON.stringify(entry.data) as its config attribute (see
Component Patterns).
Stories work the same way with getScrollytellingSchema(): see
Stories as a content collection.
Tips & Best Practices
Section titled “Tips & Best Practices”Keep Maps Simple
Section titled “Keep Maps Simple”For blog posts, simpler maps are often better. A single marker showing “where this happened” is more effective than a complex multi-layer visualization.
Use Appropriate Zoom Levels
Section titled “Use Appropriate Zoom Levels”- City overview: zoom 10-12
- Neighborhood: zoom 13-15
- Specific building/location: zoom 16-18
Consider Non-Interactive Maps
Section titled “Consider Non-Interactive Maps”For purely illustrative maps, disable interactivity to reduce visual noise:
const config = buildPointMapConfig({ location, mapStyle: MAP_STYLE, interactive: false,});Next Steps
Section titled “Next Steps”- Astro: the
<Map>and<FullPageMap>components, loaders, and deploying to GitHub Pages - Story maps in Astro: the
<Scrollytelling>component - Layers & styling for more complex visualizations
- Live data for real-time updates
- Examples for more patterns