A starter is a blank, buyer-facing version of one edition: the same shell,
theming, density, layout, direction, i18n and command palette (navigation and
appearance only — no record search, no bell, no RBAC), with every demo
screen, the mock API and their dependencies already removed. There is nothing
left to strip before your first real screen: a starter has no demo data, no
auth and no record search to begin with, because tools/sync-shared.mjs
never copies shared/mock/, shared/auth/, shared/forms/,
shared/billing/, shared/inbox/, shared/analytics/, shared/calendar/,
shared/guide/, shared/notifications/, shared/charts/ or
shared/search/{view,commands} into a starter mirror in the first place
(tools/starter/subset.mjs:1-10).
Starting one
Copy starters/<name>/ out of this repository into wherever your project
will live, then install and build it standalone, from its own lockfile, the
same way its full edition does. Copy it out rather than editing it in
place: inside this repository, tools/starter/build.mjs regenerates every
starter from its edition and overlays, and --check fails on a starter
that no longer matches what it would generate.
HTML
In starters/html
pnpm install --frozen-lockfile
pnpm run build
React
In starters/react
pnpm install --frozen-lockfile
pnpm run build
Next.js
In starters/nextjs
pnpm install --frozen-lockfile
pnpm run typecheck
pnpm run build
Vue
In starters/vue
pnpm install --frozen-lockfile
pnpm run build
Nuxt
In starters/nuxt
pnpm install --frozen-lockfile
pnpm run typecheck
pnpm run build
HTML starter
Demo screens (orders, customers, pipeline, inbox, calendar, analytics,
billing…), the mock API and its browser journal, charts, the notification
bell, record search, sign-in pages and role-based access. Add a real backend
once your first screen needs one, and read it through fetch the way
assets/js/app.js reads nothing today.
Add pages/orders.html, with front matter:
<!--@ title: Orders | layout: shell | nav: orders | crumbs: Commerce @-->.
Add a link in partials/nav.html, wrapped in {{#nav-orders}}…{{/nav-orders}}
so it highlights when active — the palette reads its pages straight off this
sidebar, so there is nothing else to wire up.
Run pnpm run build again.
Add the key to shared/locales/en.json. The mirror also carries es,
pt-BR and ar for parity with the four framework starters, but this
edition has no language switcher — it only ever renders en.
React starter
Demo screens, the mock API, charts, the notification bell, record search
inside the palette, sign-in and RBAC, and TanStack Query. Add Query back the
day a screen actually fetches something.
Add the page under src/pages/, in the shape src/pages/Home.tsx already
is: a component, no data of its own to start.
Give it a route in src/app/App.tsx, inside <AppShell>, the way / and
the catch-all already are.
Add an entry to src/app/nav.ts — the sidebar and the command palette both
read this one list, so a page shows up in both from a single edit.
Add the two keys the entry needs (its key and, if the nav item is new to
a section, the section's key) to all four catalogues under
src/shared/locales/: en.json, es.json, pt-BR.json, ar.json. A
missing key renders as the raw key rather than failing.
Next.js starter
Demo screens, the mock API, charts, the notifications bell, record search,
auth and RBAC, and TanStack Query. Add Query back when your first screen
fetches from a real API — the demo edition's src/layout/palette.tsx shows
the shape of a debounced, cancellable fetch if you need one for search later.
Add a page: src/app/[locale]/orders/page.tsx.
Add it to the nav (and the palette, which reads the same list):
Add nav.orders (and any other new keys) to all four catalogues under
shared/locales/ at the repository root, then re-run pnpm dev in the
full Orion checkout so tools/sync-shared.mjs copies them down — or, once
this starter is on its own, add the keys straight to
src/shared/locales/*.json.
Vue starter
Demo screens, the mock API, charts, the notification bell, record search,
authentication, RBAC and TanStack Query. Add a query library when your first
screen actually fetches something.
Create the page, e.g. src/pages/Reports.vue.
Add a route in src/router.ts, with a meta.title and meta.crumbs
catalogue key:
The command palette picks it up automatically — it reads the same list.
Add nav.reports (and any other new keys) to all four catalogues in
src/shared/locales/.
A route-only change to src/App.vue or src/router.ts (adding a route, not
touching the shell logic) is a legitimate reason to re-pin this starter's
overlay with tools/starter/build.mjs --repin vue <path>; it does not mean
the overlay drifted from the edition.
Nuxt starter
Every demo screen (orders, customers, products, invoices, analytics,
calendar, inbox, settings), the mock API, ApexCharts, the notification bell,
the topbar's record-search combobox, and auth/RBAC — there is no signed-in
role, so the sidebar and the palette show every page there is. Wire your own
backend and add data fetching where you need it.
Create the page, e.g. app/pages/orders.vue. Give it
definePageMeta({ title: '...', crumbs: '...' }) if it needs its own
heading, or leave it out to inherit the dashboard's.
Add an entry to SECTIONS in app/utils/nav.ts. That one list feeds both
the sidebar and the palette's navigate commands, so a page added there
shows up in both with no extra wiring.
Add any new catalogue key to the four files this starter's shared/
mirror carries at app/shared/locales/en.json, es.json, pt-BR.json
and ar.json. If you are developing inside the Orion repository, edit the
repository's own shared/locales/*.json instead and let the sync mirror
them down.
Fetch data with Nuxt's own useFetch/useAsyncData — nothing here
reserves a spot for a separate query library.
How the starters are generated
starters/html, starters/react, starters/nextjs, starters/vue and starters/nuxt
are blank, buyer-facing starting points for each edition: the same shell, theming,
density, layout, direction, RTL, i18n and command palette (Cmd/Ctrl+K, navigation and
appearance only — no record search, no bell, no RBAC), with every demo screen, the mock
API and their dependencies removed. Each installs and runs standalone, from its own
lockfile, the same way the edition it comes from does.
They are generated, never hand-edited, and their source is committed like any other
edition's: only the shared mirrors (starters/*/**/shared/), the HTML starter's vendored
bundle (starters/html/assets/vendor/), build output (dist/, .next/, .output/,
node_modules/, …) and starters/nextjs/next-env.d.ts are gitignored — the same
carve-outs each full edition has.
From README.md — Full repository only
node tools/starter/build.mjs # regenerate all five starters
node tools/starter/build.mjs react # regenerate one, by name
node tools/starter/build.mjs --check # every starter matches its manifest
node tools/starter/build.mjs --check --complete # --check, plus every starter/mirror/release-smoke name list agrees
node tools/starter/build.mjs --repin react src/layout/Topbar.tsx # take a new pin after reviewing an edition change
Each starter comes from a manifest (tools/starter/starters/<name>.json) naming what to
copy from the edition verbatim, which dependencies to drop, and which files are
overlays — a starter-only file, or a full replacement of an edition file, kept in
tools/starter/overlay/<name>/**. An overlay that replaces an edition file records a
truncated sha256 of that edition file (fromSha), so --check fails the moment the
edition file changes underneath the pin — a starter can never drift silently from the
edition it tracks. Review the edition's change, update the overlay if the starter needs
to follow it, then take the new pin with --repin; a route-only change to the edition
file, with nothing new for the starter to reconcile, is a legitimate reason to --repin
without touching the overlay at all.
A starter's command palette is built from one nav list per starter (src/app/nav.ts for
React/Vue, src/layout/sections.ts for Next.js, app/utils/nav.ts for Nuxt, the
rendered sidebar for HTML) — the same list its sidebar renders, so a page added there
shows up in both. node tools/release-smoke/run.mjs starters and
node tools/smoke/starter-html.mjs (the HTML starter's file:// pass) prove this end to
end: a clean install, a build, every route in every appearance, and an interaction pass
that opens the palette, chooses an appearance and confirms it repaints rather than just
flips an attribute, opens and closes the drawer, and — where the starter has a language
switcher — proves that too.
After an edition's own lockfile changes, refresh the matching starter's lockfile the
same way, then reinstall from it: