Skip to content
Development
Command

/portaljs-new-portal

Scaffold a new PortalJS data portal from a brief. Copies the canonical template from examples/portaljs-catalog and substitutes project tokens.

From plugin
portaljs
2.3k25 skills25 commands
Install
> /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.md
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
Read more
Ships withportaljs

🌀 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).

Get the whole plugin