/portaljs-add-map
Render a GeoJSON dataset on an interactive Leaflet map in the Views section of a dataset's showcase. Installs react-leaflet and a Map component, then renders the map for the chosen dataset.
> /plugin marketplace add datopian/portaljs > /plugin install portaljs@datopian-portaljs
How it fires
How this command gets triggered: by you, by Claude, or both.
- Fires itselfClaude auto-loads it when your prompt matches the work.
- You can call itInvoke it directly when you want it.
- Slash command
/portaljs-add-map
Context preview
What this command does when you run it.
Render a GeoJSON dataset on an interactive Leaflet map in the Views section of a dataset's showcase. Installs react-leaflet and a Map component, then renders the map for the chosen dataset.
Command definition
portaljs-add-map.mddescription: Render a GeoJSON dataset on an interactive Leaflet map in the Views section of a dataset's showcase. Installs react-leaflet and a Map component, then renders the map for the chosen dataset.
allowed-tools: Read, Write, Edit, Bash, WebFetch
/portaljs-add-map
Add an interactive Leaflet map as a **view on a dataset's showcase** in a `portaljs-catalog` portal. Installs `react-leaflet`/`leaflet` (once), generates a reusable `Map` component, and renders the map into the **Views** section of the showcase route `pages/[owner]/[slug].tsx` for the chosen GeoJSON dataset.
Use this when a dataset's data is geographic (points, lines, polygons) and you want a map view in addition to the showcase's default metadata + download. The dataset should already be registered in `datasets.json` (e.g. via `/portaljs-add-dataset`) with `format: "geojson"`; if it isn't yet, this skill can copy the file and add the entry first.
Required input — ask, don't error
- **Dataset** — which dataset to map, by **slug**. It should be a `geojson` entry in
`datasets.json`. If a source file/URL is given for a not-yet-registered dataset, the skill copies it into `/public/data/` and appends a manifest entry.
- **Portal directory** — path to the portal project (defaults to current directory).
- **Source** (only if the dataset isn't registered yet) — a local file path
(`./data/file.geojson`) or public URL. Must be **GeoJSON** (a `Feature`, `FeatureCollection`, or geometry object).
**If the target dataset isn't specified, ask which one (by name/slug) — never dead-end with a missing-input error.**
Steps
1. Gather input from `$ARGUMENTS` (interview if thin)
Extract:
- `DATASET` — dataset slug (the map target)
- `SOURCE` — file path or URL (only needed if the dataset isn't already in the manifest)
- `PORTAL_DIR` — portal directory (default: `.`)
- `MAP_SLUG` — slug for a new dataset (default: lowercase hyphenated filename)
- `MAP_NAME` — human-readable name (default: derived from filename)
- `NAMESPACE` — namespace for a new dataset (default: the catalog's existing namespace)
- `DESCRIPTION` — optional one-line description
If neither a dataset nor a source is given, **ask** and wait. When the user doesn't know the slug, read `PORTAL_DIR/datasets.json` and list the GeoJSON datasets so they can pick:
To add a map I need either:
1. Which existing dataset to map? (slug — GeoJSON datasets in your catalog: <name (slug)>, …)
…or a GeoJSON source to add and map:
2. Source: local file path or public URL to a GeoJSON file
3. Portal directory (Enter for current directory)
2. Validate the portal directory
The target must be a `portaljs-catalog` portal. Confirm `PORTAL_DIR/datasets.json`, `PORTAL_DIR/package.json`, and `PORTAL_DIR/pages/[owner]/[slug].tsx` exist. If they don't, tell the user this isn't the catalog template and ask how to proceed rather than failing silently.
3. Resolve (or register) the GeoJSON dataset
**If `DATASET` is already in `datasets.json`:** read its entry and capture `namespace`, `slug`, and `file` (must be served from `/public/data/<file>`). Confirm `format` is `geojson`; if it's tabular, tell the user `/portaljs-add-map` only renders GeoJSON and ask whether they meant a different dataset (or `/portaljs-add-chart`).
**If a `SOURCE` was given for a not-yet-registered dataset:** fetch/copy and validate it, then append a manifest entry so the showcase exists:
- URL: fetch it; if the status isn't 200, tell the user (with the HTTP status) and ask
them to confirm the URL is publicly accessible / supports CORS.
- Local path: if the file doesn't exist, tell the user and ask for a correct path.
- **Validate it is GeoJSON:** parse as JSON and confirm `type` is one of
`FeatureCollection`, `Feature`, `GeometryCollection`, `Point`, `MultiPoint`, `LineString`, `MultiLineString`, `Polygon`, or `MultiPolygon`. If not, tell the user it isn't valid GeoJSON (for tabular data use `/portaljs-add-dataset`) and stop.
- Copy and register:
mkdir -p PORTAL_DIR/public/data
cp SOURCE PORTAL_DIR/public/data/MAP_SLUG.geojson
# or for URLs: curl -L SOURCE -o PORTAL_DIR/public/data/MAP_SLUG.geojson
Then append to `datasets.json` (matching the `Dataset` shape in `lib/datasets.ts`):
{
"slug": "MAP_SLUG",
"namespace": "NAMESPACE",
"name": "MAP_NAME",
"description": "DESCRIPTION",
"file": "MAP_SLUG.geojson",
"format": "geojson"
}Capture the final `NAMESPACE`, `SLUG`, and `FILE` for use when rendering the map.
4. Install map dependencies (once)
The template does not bundle a map component. Install Leaflet directly. Check whether `react-leaflet` is already in `PORTAL_DIR/package.json` — if so, skip this step.
cd PORTAL_DIR && npm install react-leaflet@^5 leaflet@^1.9 && npm install -D @types/leaflet
Tell the user first: `Installing map dependencies (react-leaflet, leaflet)...`
If install fails, tell the user (check Node.js >=22 and network access) and retry.
> Why `react-leaflet@^5`: v5 targets React 19, which the catalog template uses. (If a > portal is still on React 18, install `react-leaflet@^4` instead — its peer dep requires > React 18.)
5. Generate the `Map` component (once)
Leaflet touches `window` at module load, so it **must not** be server-rendered. The component is split in two: `MapView.tsx` holds the Leaflet code, and `Map.tsx` is a thin wrapper that loads it with `dynamic(..., { ssr: false })`. Only the type is imported across the boundary (erased at build), so Leaflet never reaches the server bundle.
Skip this step if `PORTAL_DIR/components/Map.tsx` already exists.
Write `PORTAL_DIR/components/MapView.tsx`:
import { useEffect, useState } from 'react'
import { MapContainer, TileLayer, GeoJSON, useMap } from 'react-leaflet'
import L from 'leaflet'
import type { GeoJsonObject } from 'geojson'
import 'leaflet/dist/leaflet.css'
// Fits the viewport to the dataRead more
description: Render a GeoJSON dataset on an interactive Leaflet map in the Views section of a dataset's showcase. Installs react-leaflet and a Map component, then renders the map for the chosen dataset. allowed-tools: Read, Write, Edit, Bash, WebFetch
/portaljs-add-map
Add an interactive Leaflet map as a **view on a dataset's showcase** in a `portaljs-catalog` portal. Installs `react-leaflet`/`leaflet` (once), generates a reusable `Map` component, and renders the map into the **Views** section of the showcase route `pages/[owner]/[slug].tsx` for the chosen GeoJSON dataset.
Use this when a dataset's data is geographic (points, lines, polygons) and you want a map view in addition to the showcase's default metadata + download. The dataset should already be registered in `datasets.json` (e.g. via `/portaljs-add-dataset`) with `format: "geojson"`; if it isn't yet, this skill can copy the file and add the entry first.
Required input — ask, don't error
- **Dataset** — which dataset to map, by **slug**. It should be a `geojson` entry in
`datasets.json`. If a source file/URL is given for a not-yet-registered dataset, the skill copies it into `/public/data/` and appends a manifest entry.
- **Portal directory** — path to the portal project (defaults to current directory).
- **Source** (only if the dataset isn't registered yet) — a local file path
(`./data/file.geojson`) or public URL. Must be **GeoJSON** (a `Feature`, `FeatureCollection`, or geometry object).
**If the target dataset isn't specified, ask which one (by name/slug) — never dead-end with a missing-input error.**
Steps
1. Gather input from `$ARGUMENTS` (interview if thin)
Extract:
- `DATASET` — dataset slug (the map target)
- `SOURCE` — file path or URL (only needed if the dataset isn't already in the manifest)
- `PORTAL_DIR` — portal directory (default: `.`)
- `MAP_SLUG` — slug for a new dataset (default: lowercase hyphenated filename)
- `MAP_NAME` — human-readable name (default: derived from filename)
- `NAMESPACE` — namespace for a new dataset (default: the catalog's existing namespace)
- `DESCRIPTION` — optional one-line description
If neither a dataset nor a source is given, **ask** and wait. When the user doesn't know the slug, read `PORTAL_DIR/datasets.json` and list the GeoJSON datasets so they can pick:
To add a map I need either: 1. Which existing dataset to map? (slug — GeoJSON datasets in your catalog: <name (slug)>, …) …or a GeoJSON source to add and map: 2. Source: local file path or public URL to a GeoJSON file 3. Portal directory (Enter for current directory)
2. Validate the portal directory
The target must be a `portaljs-catalog` portal. Confirm `PORTAL_DIR/datasets.json`, `PORTAL_DIR/package.json`, and `PORTAL_DIR/pages/[owner]/[slug].tsx` exist. If they don't, tell the user this isn't the catalog template and ask how to proceed rather than failing silently.
3. Resolve (or register) the GeoJSON dataset
**If `DATASET` is already in `datasets.json`:** read its entry and capture `namespace`, `slug`, and `file` (must be served from `/public/data/<file>`). Confirm `format` is `geojson`; if it's tabular, tell the user `/portaljs-add-map` only renders GeoJSON and ask whether they meant a different dataset (or `/portaljs-add-chart`).
**If a `SOURCE` was given for a not-yet-registered dataset:** fetch/copy and validate it, then append a manifest entry so the showcase exists:
- URL: fetch it; if the status isn't 200, tell the user (with the HTTP status) and ask
them to confirm the URL is publicly accessible / supports CORS.
- Local path: if the file doesn't exist, tell the user and ask for a correct path.
- **Validate it is GeoJSON:** parse as JSON and confirm `type` is one of
`FeatureCollection`, `Feature`, `GeometryCollection`, `Point`, `MultiPoint`, `LineString`, `MultiLineString`, `Polygon`, or `MultiPolygon`. If not, tell the user it isn't valid GeoJSON (for tabular data use `/portaljs-add-dataset`) and stop.
- Copy and register:
mkdir -p PORTAL_DIR/public/data cp SOURCE PORTAL_DIR/public/data/MAP_SLUG.geojson # or for URLs: curl -L SOURCE -o PORTAL_DIR/public/data/MAP_SLUG.geojson
Then append to `datasets.json` (matching the `Dataset` shape in `lib/datasets.ts`):
{
"slug": "MAP_SLUG",
"namespace": "NAMESPACE",
"name": "MAP_NAME",
"description": "DESCRIPTION",
"file": "MAP_SLUG.geojson",
"format": "geojson"
}Capture the final `NAMESPACE`, `SLUG`, and `FILE` for use when rendering the map.
4. Install map dependencies (once)
The template does not bundle a map component. Install Leaflet directly. Check whether `react-leaflet` is already in `PORTAL_DIR/package.json` — if so, skip this step.
cd PORTAL_DIR && npm install react-leaflet@^5 leaflet@^1.9 && npm install -D @types/leaflet
Tell the user first: `Installing map dependencies (react-leaflet, leaflet)...`
If install fails, tell the user (check Node.js >=22 and network access) and retry.
> Why `react-leaflet@^5`: v5 targets React 19, which the catalog template uses. (If a > portal is still on React 18, install `react-leaflet@^4` instead — its peer dep requires > React 18.)
5. Generate the `Map` component (once)
Leaflet touches `window` at module load, so it **must not** be server-rendered. The component is split in two: `MapView.tsx` holds the Leaflet code, and `Map.tsx` is a thin wrapper that loads it with `dynamic(..., { ssr: false })`. Only the type is imported across the boundary (erased at build), so Leaflet never reaches the server bundle.
Skip this step if `PORTAL_DIR/components/Map.tsx` already exists.
Write `PORTAL_DIR/components/MapView.tsx`:
import { useEffect, useState } from 'react'
import { MapContainer, TileLayer, GeoJSON, useMap } from 'react-leaflet'
import L from 'leaflet'
import type { GeoJsonObject } from 'geojson'
import 'leaflet/dist/leaflet.css'
// Fits the viewport to the data🌀 AI-native framework for building data portals. Scaffold a full portal from a brief and load datasets in minutes with agentic skills — any backend (CKAN, GitHub, Frictionless).
Repo: datopian/portaljs
Other commands on portaljs.
- /add-chart
Renamed → /portaljs-add-chart. This alias will be removed next minor release.
Open command - /add-dataset
Renamed → /portaljs-add-dataset. This alias will be removed next minor release.
Open command - /add-map
Renamed → /portaljs-add-map. This alias will be removed next minor release.
Open command - /add-resource
Renamed → /portaljs-add-resource. This alias will be removed next minor release.
Open command - /arcgis-to-portaljs
Migrate a whole ArcGIS Hub site (opendata.arcgis.com or a Hub Premium custom domain) into a PortalJS Arc portal end-to-end. Harvests the Hub /data.json (DCAT-US) inventory, exports every FeatureService layer through the ArcGIS REST query API (resultOffset paging), converts each
Open command - /architect
Renamed → /portaljs-architect. This alias will be removed next minor release.
Open command

