Skip to main content

Scaffold a framework starter

shellui init scaffolds a Shellui project and, for each JS framework, a companion iframe app already wired for theme and language. This page covers what init writes, the shared SDK pattern, and how each starter runs.

What shellui init writes

Run the wizard or pass a framework id:

shellui init
shellui init react
shellui init --framework next --backend none

Init does the following:

  1. Writes shellui.config.json (with $schema) - port 4000, layout, theme "shellui", Home nav, Settings modal
  2. Adds placeholder files under static/ (favicon.svg, logo.svg, icons)
  3. For JS frameworks: copies a companion starter, sets dev.run / dev.url, and runs your package manager install unless --no-install
  4. Points Home url at the companion origin so the shell loads the iframe app

Templates come from the GitHub tag that matches the CLI version (or a local monorepo copy). They are not bundled in the npm tarball. Overwrite an existing config with --force.

Supported positional ids: empty, react, vue, angular, next, nuxt, svelte, alpine. The wizard also offers Other (coming soon), which writes a shell-only config with static/ stubs and no framework template (same companion shape as empty). Command flags and backends are on CLI init. First-run flow is on Create a project.

Theme and i18n out of the box

Every JS starter uses @shellui/sdk/tiny the same way you would wire it by hand:

  1. Wait for shellui.ready
  2. Call shellui.applyTheme() and subscribe with shellui.on('theme', …)
  3. Read shellui.language, subscribe with shellui.on('language', …), and drive a local i18n library (or a small message map)
import { shellui } from '@shellui/sdk/tiny';

void shellui.ready.then(() => {
shellui.applyTheme();
applyLanguage(shellui.language);
});

shellui.on('theme', () => shellui.applyTheme());
shellui.on('language', applyLanguage);

Sample catalogs ship as en / fr. Change theme or language in Shell Settings and the companion home page updates. Framework-specific wiring (hooks, composables, services, plugins) is under each starter below. Related: Themes, Internationalization, SDK.

Choose a framework

Pick a starter, then jump to its section for ports and stack notes:

After init, from the project root:

shellui start

That starts the shell on 4000 and the companion on the port in the table below. Init sets dev.run to {pm} run dev for every JS starter. Run the companion alone with that script if you need it without the host.

FrameworkInit idCompanion dev.url
React / Vue / SvelteKit / Alpinereact, vue, svelte, alpinehttp://localhost:5173
Angularangularhttp://localhost:4200
Next.js / Nuxtnext, nuxthttp://localhost:3000
Emptyemptynone (Home at /)

React

Vite + React starter; init id react, companion port 5173.

shellui init react
shellui start

Theme and i18n in this starter:

  • Theme / i18n: src/useShellui.js calls shellui.applyTheme() and syncs language into i18next (src/i18n.js, react-i18next)
  • Stack note: the hook returns { theme, language, shellui } for the home UI

Vue

Vite + Vue starter; init id vue, companion port 5173.

shellui init vue
shellui start

Theme and i18n in this starter:

  • Theme / i18n: src/composables/useShellui.js updates vue-i18n locale from shellui.on('language', …)
  • Stack note: main.js registers the i18n plugin; the composable owns the SDK listeners

Angular

Angular starter; init id angular, companion port 4200. Init sets dev.run to {pm} run dev (the package dev script runs ng serve --port 4200).

shellui init angular
shellui start

Theme and i18n in this starter:

  • Theme / i18n: ShelluiService (src/app/shellui.service.ts) applies theme and resolves strings with t() over inline en / fr maps in src/app/i18n.ts
  • Stack note: the root component starts the service on init; signals hold theme and language

Next.js

Next.js App Router starter; init id next, companion port 3000 (pinned via scripts/ensure-port.mjs).

shellui init next
shellui start

Theme and i18n in this starter:

  • Theme / i18n: client component app/home.js dynamic-imports @shellui/sdk/tiny (SSR-safe); app/i18n.js holds a plain t(lang, key) helper
  • Stack note: keep SDK work in client components; do not import the tiny SDK at the top level of a Server Component

Nuxt

Nuxt starter; init id nuxt, companion port 3000 (nuxt.config.ts devServer.port).

shellui init nuxt
shellui start

Theme and i18n in this starter:

  • Theme / i18n: client plugin app/plugins/shellui.client.ts writes shared state; app/composables/useShellui.ts reads useState('shellui-theme' | 'shellui-language')
  • Stack note: Nuxt 4 expects a recent Node 22.x / 24.x

SvelteKit

SvelteKit starter; init id svelte, companion port 5173.

shellui init svelte
shellui start

Theme and i18n in this starter:

  • Theme / i18n: src/lib/shellui.js starts listeners from +layout.svelte onMount; src/lib/i18n.js uses writable / derived stores
  • Stack note: dynamic import keeps the SDK off the server render path

Alpine.js

Vite + Alpine.js starter; init id alpine, companion port 5173.

shellui init alpine
shellui start

Theme and i18n in this starter:

  • Theme / i18n: src/main.js registers Alpine.data('shelluiHome', …) with ready / theme / language handlers; src/i18n.js exports t()
  • Stack note: init also sets language: ["en", "fr"] in config so Settings lists both locales. No Tailwind in this starter

Empty shell

Shell-only project; init id empty. No companion, no dev block. Home stays at /.

shellui init empty
shellui start

Add your own iframe app later and set dev.run / dev.url - see companion process.

Companion checklist

After shellui start:

  1. Open http://localhost:4000/ - the shell loads the companion in Home
  2. Open Shell Settings and switch theme - the iframe page should restyle
  3. Switch language to French - sample strings should update

If the companion port is busy, free it or change both the framework config and dev.url together. Picked Other in the wizard? You get a shell-only config like empty; wire your own companion the same way. Details: Create a project, CLI.