type: guide title: "Scaffold a new app" description: "npm create honua-app: what the template generates, which versions it pins, and what to change first." resource: "https://www.npmjs.com/package/create-honua-app"
create-honua-app
create-honua-app scaffolds a Vite + TypeScript application that already connects to a geospatial endpoint and mounts
a source on MapLibre. It exists so the first minute with the SDK is spent reading a working workflow instead of
assembling peer dependencies.
npm create honua-app@latest my-map -- --sdk-version 0.2.3 # x-release-please-version
cd my-map
npm install
npm run dev
The starter package lives in this repository at packages/create-honua-app. The commands on this page explicitly
pin the SDK version documented here; omit --sdk-version to use the promoted channel or certified fallback below.
Which SDK version a scaffold pins
templates.manifest.json names the release channel the starters follow (sdk.channel, the release-2026.1 npm
dist-tag) and a certified fallback (sdk.version). At scaffold time the CLI reads the channel from the registry npm is
configured to use. Under npm create or npx it asks npm itself (npm view), so scope registries, .npmrc
authentication, and proxy settings apply exactly as they will for npm install. It pins the channel's version only when that version is a stable release on the starter's SDK line:
the same major, and the same minor while the SDK is 0.x. Release promotion is the only thing that moves the tag, so
an SDK promotion reaches new scaffolds without a create-honua-app republish.
In every other case the scaffold pins the certified fallback and prints why: the channel is not promoted yet, it names
a prerelease or another line, or the registry cannot be read. A registry that answers but has no SDK package, or lists
neither the channel version nor the certified fallback, fails the scaffold, because that app could never install. The tag is not named 2026.1 because npm refuses a
dist-tag that is also a valid semver range. --sdk-version <exact> overrides the channel. It is the only way to
scaffold a prerelease, and the CLI refuses a version the registry does not list.
npm run create-app:verify:package and test/scripts/create-honua-app-package.test.mjs scaffold from the packed
tarball against a loopback registry and assert the pinned version for each channel state.
Templates
| Template | Entry point | What it demonstrates |
|---|---|---|
vanilla-ts (default) |
src/main.ts |
connect → inspect → explain → query → mount. The SDK owns the MapLibre map and mounts an accepted query plan. |
react-ts |
src/App.tsx |
The app owns a plain maplibre-gl map; the same kernel connection inspects, explains, queries, and mounts onto it. |
Both templates pin MapLibre GL JS 6.1.0, the current major. A scaffolded app installs @honua/sdk-js from the
registry, which declares the ^5.0.0 || ^6.0.0 peer range, so npm install resolves the pair with no overrides and
no --legacy-peer-deps. MapLibre 5 remains supported by the SDK for apps that have not migrated. Because MapLibre 6 is
ESM-only and loads its worker as a separate module, each starter ships src/maplibre-worker.ts and imports it before
the first map is created. See maplibre-runtime.md.
npm create honua-app@latest my-map -- --template react-ts --sdk-version 0.2.3 # x-release-please-version
create-honua-app --list-templates
The copyable core of the vanilla starter is the published workflow, not a private shortcut:
import { createHonua } from "@honua/sdk-js";
import { maplibreRenderer } from "@honua/sdk-js/runtime";
import * as maplibregl from "maplibre-gl";
const honua = createHonua();
const connection = await honua.connect(
{ url: endpoint, protocol: "geoservices-feature-service" },
{ authorizationScopeFingerprint: "anonymous-public" },
);
const inspection = await connection.inspect();
const sourceId = inspection.defaultSourceId ?? inspection.sources[0]?.descriptor.id;
const plan = await connection.explain({ returnGeometry: true, pagination: { limit: 250 } }, { sourceId });
const mounted = await connection.mount("#map", { renderer: maplibreRenderer(maplibregl), query: plan, sourceId });
await mounted.ready;
The fixture-first default
Both starters ship a committed GeoServices fixture (the reviewed First Map sample fixture) and serve it from the Vite dev and preview servers. The default lane therefore needs no account, no API key, and no third-party network call — which is also what makes the starters runnable in a browser playground. See Zero-install playgrounds.
Set VITE_HONUA_ENDPOINT (and optionally VITE_HONUA_PROTOCOL) to run the identical code against any anonymous,
CORS-enabled GeoServices FeatureServer layer or OGC API Features landing page. Durable credentials never belong in
Vite environment variables: Vite embeds them in public JavaScript.
Published default verification
The independently managed create-honua-app@0.2.2 release was verified from the public registry with both starters
and no SDK override. The observed public latest CLI selected its certified SDK 0.2.2 fallback. Vanilla reached
a rendered map in 88.294 seconds and React in 86.196 seconds; each measurement includes registry resolution, CLI
installation, scaffolding, fresh default-resolution app installation, production build and browser startup. Both stayed
inside the unchanged 120-second budget, rendered four fixture features, and recorded no console errors or off-origin requests.
The retained default proof binds the actual npm tarballs,
installed lockfiles and independent source tags. The published scaffold README is byte-identical to its own
create-honua-app-v0.2.2 source, independently of js-sdk-v0.2.2. The original
SDK 0.2.1 explicit-template proofs and
later SDK 0.2.2 proofs remain unchanged.
These starter fixture journeys do not qualify a live server candidate.