Skip to content

/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.4k25 skills25 commands
> /plugin marketplace add datopian/portaljs
> /plugin install portaljs@datopian-portaljs

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 and the UI language, 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.

Every UI string (navigation, search, showcase, SQL/map panels, errors) lives in `locales/en.json` and renders through `t()` from `lib/i18n.ts`. A portal has **one** UI language, committed as `DEFAULT_LOCALE` in `next.config.js` (default `'en'`); another language is a sibling `locales/<code>.json` with the same keys, registered in `lib/i18n.ts`. `npm run i18n:check` validates the locale files.

Required input — interview, don't error

You need a **project name** and **one-line description**. Datasets are optional at scaffold time, and the UI language defaults to English. **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)
4. Which language should the interface be in? (default: English — a name or a code,
   e.g. "Italian" or "it", "Brazilian Portuguese" or "pt-BR")

Infer the language when the brief already says it (e.g. "build an Italian data portal", or a name/description written in Italian) and confirm it in Step 2 instead of asking. Write the name and description in the portal's language — they are shown as-is.

**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."`; language English (`en`); 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
  • Language:    LANGUAGE  (locale LOCALE_CODE, formats INTL_TAG — e.g. Italian: it, it-IT)
  • 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 char

Read more
@datopian
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).

Stars on GitHub

Momentum

stars in total
2.4k
Star history of datopian/portaljs on GitHub, a chart by star-history.com

Other commands on portaljs.

add-chart
Auto-invoked

add-chart

Command

Renamed → /portaljs-add-chart. This alias will be removed next minor release.

@datopian@datopianInstall Command
add-dataset
Auto-invoked

add-dataset

Command

Renamed → /portaljs-add-dataset. This alias will be removed next minor release.

@datopian@datopianInstall Command
add-map
Auto-invoked

add-map

Command

Renamed → /portaljs-add-map. This alias will be removed next minor release.

@datopian@datopianInstall Command
add-resource
Auto-invoked

add-resource

Command

Renamed → /portaljs-add-resource. This alias will be removed next minor release.

@datopian@datopianInstall Command
arcgis-to-portaljs
Auto-invoked

arcgis-to-portaljs

Command

Migrate a whole ArcGIS Hub site (opendata.arcgis.com or a Hub Premium custom domain) into a PortalJS Arc portal end-to-end.…

@datopian@datopianInstall Command
architect
Auto-invoked

architect

Command

Renamed → /portaljs-architect. This alias will be removed next minor release.

@datopian@datopianInstall Command

More commands from other plugins.

build
Auto-invoked

build

Command

Implement tasks incrementally — build, test, verify, commit. Add "auto" to run the whole plan in one approved pass.

@addyosmani@addyosmaniInstall Command