/portaljs-new-portal
Scaffold a new PortalJS data portal from a brief. Copies the canonical template from examples/portaljs-catalog and substitutes project tokens.
> /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-new-portal
Context preview
What this command does when you run it.
Scaffold a new PortalJS data portal from a brief. Copies the canonical template from examples/portaljs-catalog and substitutes project tokens.
Command definition
portaljs-new-portal.mddescription: Scaffold a new PortalJS data portal from a brief. Copies the canonical template from examples/portaljs-catalog and substitutes project tokens.
allowed-tools: Read, Write, Edit, Bash
/portaljs-new-portal
Scaffold a production-ready PortalJS data portal. The skill is **interactive**: if the brief is thin, it interviews the user in three short rounds (mapped to the template's three surfaces — Home, Catalog, Showcase), echoes a brief back for confirmation, then copies `examples/portaljs-catalog`, substitutes placeholder tokens, sets the namespace mode, seeds any datasets, installs dependencies, and verifies the build.
The template you are scaffolding
`examples/portaljs-catalog` is the canonical template. It has **three surfaces**:
| Surface | Route | File | What it is | |---|---|---|---| | Home | `/` | `pages/index.tsx` | Search-first landing: hero + search box + suggested-query chips (`__PROJECT_NAME__` / `__DESCRIPTION__` tokens) | | Catalog / search | `/search` | `pages/search.tsx` | Client-side full-text list over `datasets.json` | | Dataset showcase | `/@<namespace>/<slug>` | `pages/[owner]/[slug].tsx` | One dataset: metadata, `Table` data preview, Download & API, and a Views placeholder for charts/maps |
The catalog is driven by `datasets.json` (the single source of truth) — each entry is `{ slug, name, description, file, format, namespace }`. `lib/datasets.ts` exposes `getDatasets()`, `getDataset(slug)`, `getDatasetByNamespace(ns, slug)`, `datasetHref(d)` → `/@${namespace}/${slug}`, and a `NAMESPACE_TYPE: 'theme' | 'owner'` constant.
A portal uses **exactly one** namespace mode:
- **`'theme'`** — a single-publisher portal whose datasets are grouped by subject
(e.g. `@reference/country-codes`). The showcase labels the namespace "Theme".
- **`'owner'`** — a multi-publisher portal whose datasets are grouped by who published
them (e.g. `@worldbank/country-codes`). The showcase labels the namespace "Owner".
Search is a **static client-side list** for now. A live backend (e.g. CKAN, via `/portaljs-connect-ckan`) can replace `datasets.json` later without changing the URL structure.
Required input — interview, don't error
You need a **project name** and **one-line description**. Datasets are optional at scaffold time. **Never dead-end with a missing-input error.** If `$ARGUMENTS` is empty or thin, run the structured interview in Step 1 to gather what's missing. The user can say **"use defaults"** at any point to skip a round and accept the defaults below.
Steps
1. Interview the user (skip rounds already answered by `$ARGUMENTS`)
Parse `$ARGUMENTS` first and pre-fill anything it already specifies. Then ask only for what's still missing, **one focused round at a time** (wait for the answer before the next round). Each round maps to a template surface. Tell the user they can reply "use defaults" to take the defaults.
**Round 1 — Home / basics:**
Round 1 of 3 — the home page.
1. What's the portal called? (e.g. "Auckland Open Data Portal")
2. One-line description for the hero? (default: "An open data portal.")
3. Who is it for / what's it about? (helps pick suggested-search chips)
**Round 2 — Catalog & discovery:**
Round 2 of 3 — the catalog (the /search list).
1. Which datasets should it start with? Give file paths or URLs, or "none yet".
2. Roughly how many datasets total — a handful, dozens, hundreds?
3. Single publisher or multiple?
- single → datasets grouped by SUBJECT (namespace mode "theme")
- multiple → datasets grouped by PUBLISHER (namespace mode "owner")
Then: what namespace value(s)? (e.g. "reference", or "worldbank, eurostat")
Note for the user when relevant: search is a static client-side list over `datasets.json` for now; a live backend (CKAN) can be wired in later with `/portaljs-connect-ckan` without changing URLs.
**Round 3 — Showcase / views:**
Round 3 of 3 — each dataset's showcase page (/@<namespace>/<slug>).
Every showcase already shows metadata + a data preview + a Download & API section.
For the dataset(s) above, do any need an extra view?
- a chart (line/bar/area/pie/scatter) → added later with /portaljs-add-chart
- a map (GeoJSON on Leaflet) → added later with /portaljs-add-map
(Press Enter / "use defaults" for metadata + preview + download only.)
**Defaults if a round is skipped:** description `"An open data portal."`; no datasets; `NAMESPACE_TYPE = 'theme'` with namespace value `reference`; no extra views.
2. Confirm the brief before building
Echo a short brief back and wait for a yes:
Here's the plan — say "go" to build, or correct anything:
• Name: PROJECT_NAME (slug: PROJECT_SLUG)
• Description: DESCRIPTION
• Namespace: NAMESPACE_TYPE — value(s): NS_VALUES
• Datasets: <list, or "none — add later with /portaljs-add-dataset">
• Views to add after scaffold: <charts/maps per dataset, or "none">
Derive `PROJECT_SLUG` from `PROJECT_NAME` (lowercase, hyphenated, no special chars, e.g. `auckland-open-data`).
3. Resolve the template source
This skill works **both inside a clone of the portaljs repo and from any other project**. **Default to fetching the template remotely (always the latest).** Use a local checkout only when you are genuinely inside an up-to-date portaljs repo AND not scaffolding into it. The default — and currently only — variant is `examples/portaljs-catalog`.
TEMPLATE_VARIANT="examples/portaljs-catalog"
# Default: fetch the latest template from GitHub. Override the ref with
# PORTALJS_TEMPLATE_REF (branch, tag, or commit); defaults to main.
TEMPLATE_MODE="remote"
PORTALJS_TEMPLATE_REF="${PORTALJS_TEMPLATE_REF:-main}"
# Use a LOCAL checkout only when BOTH hold (otherwise stay remote):
# 1. the resolved repo has the CURRENT template — verified by the @-namespaced
# route file pages/[owner]/[slug].tsx. An old clone without it would scaffold a
# stale, non-namespaced portal (pages/datasets/[slugRead more
description: Scaffold a new PortalJS data portal from a brief. Copies the canonical template from examples/portaljs-catalog and substitutes project tokens. allowed-tools: Read, Write, Edit, Bash
/portaljs-new-portal
Scaffold a production-ready PortalJS data portal. The skill is **interactive**: if the brief is thin, it interviews the user in three short rounds (mapped to the template's three surfaces — Home, Catalog, Showcase), echoes a brief back for confirmation, then copies `examples/portaljs-catalog`, substitutes placeholder tokens, sets the namespace mode, seeds any datasets, installs dependencies, and verifies the build.
The template you are scaffolding
`examples/portaljs-catalog` is the canonical template. It has **three surfaces**:
| Surface | Route | File | What it is | |---|---|---|---| | Home | `/` | `pages/index.tsx` | Search-first landing: hero + search box + suggested-query chips (`__PROJECT_NAME__` / `__DESCRIPTION__` tokens) | | Catalog / search | `/search` | `pages/search.tsx` | Client-side full-text list over `datasets.json` | | Dataset showcase | `/@<namespace>/<slug>` | `pages/[owner]/[slug].tsx` | One dataset: metadata, `Table` data preview, Download & API, and a Views placeholder for charts/maps |
The catalog is driven by `datasets.json` (the single source of truth) — each entry is `{ slug, name, description, file, format, namespace }`. `lib/datasets.ts` exposes `getDatasets()`, `getDataset(slug)`, `getDatasetByNamespace(ns, slug)`, `datasetHref(d)` → `/@${namespace}/${slug}`, and a `NAMESPACE_TYPE: 'theme' | 'owner'` constant.
A portal uses **exactly one** namespace mode:
- **`'theme'`** — a single-publisher portal whose datasets are grouped by subject
(e.g. `@reference/country-codes`). The showcase labels the namespace "Theme".
- **`'owner'`** — a multi-publisher portal whose datasets are grouped by who published
them (e.g. `@worldbank/country-codes`). The showcase labels the namespace "Owner".
Search is a **static client-side list** for now. A live backend (e.g. CKAN, via `/portaljs-connect-ckan`) can replace `datasets.json` later without changing the URL structure.
Required input — interview, don't error
You need a **project name** and **one-line description**. Datasets are optional at scaffold time. **Never dead-end with a missing-input error.** If `$ARGUMENTS` is empty or thin, run the structured interview in Step 1 to gather what's missing. The user can say **"use defaults"** at any point to skip a round and accept the defaults below.
Steps
1. Interview the user (skip rounds already answered by `$ARGUMENTS`)
Parse `$ARGUMENTS` first and pre-fill anything it already specifies. Then ask only for what's still missing, **one focused round at a time** (wait for the answer before the next round). Each round maps to a template surface. Tell the user they can reply "use defaults" to take the defaults.
**Round 1 — Home / basics:**
Round 1 of 3 — the home page. 1. What's the portal called? (e.g. "Auckland Open Data Portal") 2. One-line description for the hero? (default: "An open data portal.") 3. Who is it for / what's it about? (helps pick suggested-search chips)
**Round 2 — Catalog & discovery:**
Round 2 of 3 — the catalog (the /search list). 1. Which datasets should it start with? Give file paths or URLs, or "none yet". 2. Roughly how many datasets total — a handful, dozens, hundreds? 3. Single publisher or multiple? - single → datasets grouped by SUBJECT (namespace mode "theme") - multiple → datasets grouped by PUBLISHER (namespace mode "owner") Then: what namespace value(s)? (e.g. "reference", or "worldbank, eurostat")
Note for the user when relevant: search is a static client-side list over `datasets.json` for now; a live backend (CKAN) can be wired in later with `/portaljs-connect-ckan` without changing URLs.
**Round 3 — Showcase / views:**
Round 3 of 3 — each dataset's showcase page (/@<namespace>/<slug>). Every showcase already shows metadata + a data preview + a Download & API section. For the dataset(s) above, do any need an extra view? - a chart (line/bar/area/pie/scatter) → added later with /portaljs-add-chart - a map (GeoJSON on Leaflet) → added later with /portaljs-add-map (Press Enter / "use defaults" for metadata + preview + download only.)
**Defaults if a round is skipped:** description `"An open data portal."`; no datasets; `NAMESPACE_TYPE = 'theme'` with namespace value `reference`; no extra views.
2. Confirm the brief before building
Echo a short brief back and wait for a yes:
Here's the plan — say "go" to build, or correct anything: • Name: PROJECT_NAME (slug: PROJECT_SLUG) • Description: DESCRIPTION • Namespace: NAMESPACE_TYPE — value(s): NS_VALUES • Datasets: <list, or "none — add later with /portaljs-add-dataset"> • Views to add after scaffold: <charts/maps per dataset, or "none">
Derive `PROJECT_SLUG` from `PROJECT_NAME` (lowercase, hyphenated, no special chars, e.g. `auckland-open-data`).
3. Resolve the template source
This skill works **both inside a clone of the portaljs repo and from any other project**. **Default to fetching the template remotely (always the latest).** Use a local checkout only when you are genuinely inside an up-to-date portaljs repo AND not scaffolding into it. The default — and currently only — variant is `examples/portaljs-catalog`.
TEMPLATE_VARIANT="examples/portaljs-catalog"
# Default: fetch the latest template from GitHub. Override the ref with
# PORTALJS_TEMPLATE_REF (branch, tag, or commit); defaults to main.
TEMPLATE_MODE="remote"
PORTALJS_TEMPLATE_REF="${PORTALJS_TEMPLATE_REF:-main}"
# Use a LOCAL checkout only when BOTH hold (otherwise stay remote):
# 1. the resolved repo has the CURRENT template — verified by the @-namespaced
# route file pages/[owner]/[slug].tsx. An old clone without it would scaffold a
# stale, non-namespaced portal (pages/datasets/[slug🌀 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

