Compose Your Stack
Packs are self-contained feature modules you install into an existing project. Each pack brings its own schema, routes,
UI components, and migrations. The compose command lets you pick several at once and installs them in the right order.
Usage
npx @hype-stack/cli composeThis opens an interactive picker that walks the three categories in order: one starter, one layout, then any feature
packs. Billing is a one-of group, since a project takes a single provider. Installing anything needs a Hype Stack
account, free packs included; compose opens the browser to sign in if you aren't. Free packs stay free. Paid packs you
own are marked as owned; paid packs you don't own yet show their price and a lock.
Run it in a directory with no project and compose scaffolds one first with the create wizard, then
installs your picks into it.
Use the wizard
You rarely need to hand-build the full command. Run npx @hype-stack/cli compose with no flags and the wizard walks you
through picking packs, resolving dependencies, and handling licenses. Reach for the flags below only in CI or scripts
where prompts aren't an option.
For CI or scripts, pass the packs directly:
npx @hype-stack/cli compose --packs starter-saas-workos,pack-teams,pack-projects --yesPack families
Three groups of packs are interchangeable variants of one family: starters (starter-saas), layouts (layout), and
billing (pack-billing). A family has one slot in a project. Ask for a second variant and compose refuses, with one
exception: swapping a starter for one that provides more (the free starter-auth-betterauth to
starter-saas-betterauth or starter-saas-workos) is an upgrade. The CLI replaces the files both starters ship,
removes the ones only the old one had, and re-applies your layout. Your own files are never touched.
Packs depend on families, not on one engine. A calendar pack that needs a starter says starter-saas, and whichever
starter your project has satisfies it.
Admin layout
When your project has an admin app, the picker asks once whether to copy the frontend layout for it, defaulting to yes. Say no and you get the full admin layout list, limited to the layouts the admin shell can actually render. To pin both sides yourself, suffix the pack with the shell it targets:
npx @hype-stack/cli compose --packs layout-glass@frontend,layout-basic@adminTheme, radius, and mode
compose takes the same look-and-feel flags as create, so one command reproduces a stack you built
in the live preview:
npx @hype-stack/cli compose --packs starter-saas-workos,layout-basic@frontend,pack-teams --theme ocean --radius 0.75rem --mode dark| Flag | What it does |
|---|---|
-t, --theme <theme> | Frontend color theme. |
--admin-theme <theme> | Admin color theme. Defaults to --theme. |
--mobile-theme <theme> | Mobile app color theme. Defaults to --theme. |
--extension-theme <theme> | Extension color theme. Defaults to --theme. |
--radius <value> | Frontend corner radius as a CSS length, e.g. 0.75rem. |
--admin-radius <value> | Admin corner radius. Defaults to --radius. |
--mobile-radius <value> | Mobile app corner radius. Defaults to --radius. |
--extension-radius <value> | Extension corner radius. Defaults to --radius. |
--mode <light|dark> | Color mode the app boots in. |
In an existing project compose only rewrites what you named: a plain compose never touches your styles.css.
All flags
| Flag | What it does |
|---|---|
--packs <a,b,c> | Install these packs without the picker. Layouts take an @frontend or @admin suffix. |
--community <source> | Also install a community pack. Repeatable. |
--force | Overwrite files that already exist. Without it you are asked per file, and --yes keeps yours. |
-y, --yes | Skip confirmations. Keeps existing files, answers yes to migrations. |
--cwd <path> | Run against a different working directory. |
What happens during install
- Dependency resolution - any packs your selection depends on are pulled in and ordered automatically, with family dependencies pinned to the variant your project already has
- Capability check - a pack that needs organizations or invitations is refused by name if no installed or selected starter provides them
- License check - paid packs are authorized against your license before a byte is downloaded. If something is locked, the CLI drops what you cannot install and shows where to buy it
- Validation - the whole plan is checked before a single file is written, so a bad install never leaves a half-modified project. Prisma models that clash with one already in your schema abort with both file names
- File install - schema, backend routes, and frontend pages are copied into the right places, in dependency order, then codemods register routes, nav entries, settings tabs, jobs, and permissions
- npm install - every pack's dependencies are added in one pass per workspace
- Secrets - env variables a pack marks as generated (an auth secret, a cookie password) are rolled for you
- Migrations -
prisma generatealways runs. If the packs added models, you are asked to runprisma migrate devnow. Installs that add no models do not ask - Config update -
stack.jsonrecords each installed pack and where it came from
Translations
Packs ship no hardcoded UI copy. Every string a pack renders comes from a
Paraglide message catalog, and install merges each pack's
messages into your app's messages/en.json instead of overwriting it. Two packs can contribute to the same file, and
re-running compose produces no diff for messages you already have.
// apps/frontend/messages/en.json, after installing teams and billing
{
"$schema": "https://inlang.com/schema/inlang-message-format",
"app_title": "Acme",
"teams_page_title": "Teams",
"billing_upgrade_cta": "Upgrade"
}Sidebar entries and settings tabs are merged the same way, so a pack's nav label is translatable too rather than being
frozen into nav.constants.ts.
To add a language, add its code to locales in project.inlang/settings.json, drop a messages/<locale>.json next to
the English one, and translate the values. Anything you have not translated yet falls back to English. Only en.json is
merged on install, so your other locale files are never touched. Rewording a pack's English in en.json is fine, just
know that re-installing that pack puts its own wording back.
Access tokens
Paid packs need access. Log in once with your browser:
npx @hype-stack/cli loginThat stores a token that expires after 90 days. For CI, create an API key on the API keys page and expose it as an env var. No flag, no secret in your shell history:
HYPE_STACK_TOKEN=hsk_ci_xxx npx @hype-stack/cli compose --packs pack-billing-stripe --yesAPI keys are revocable and shown in full only once, so a leaked key can be killed without touching your license.
Community packs
Packs you wrote, or someone else published outside our catalog, install through the same pipeline from a folder, a git repo, an npm package, or a registry namespace:
npx @hype-stack/cli community add ../pack-analytics
npx @hype-stack/cli compose --packs pack-teams --community github:you/pack-analytics#v1.0.0Anything that is not a local folder gets a confirmation that defaults to no. See Build your own pack.
Removing a pack
Packs are designed to be ejected. Once installed, the code lives in your project. Delete the files and revert the schema changes to remove a pack. The CLI doesn't automate removal, since most teams customize pack code after installing it.
Next
At the end of a compose run the CLI offers to walk you through onboard, which fills in the env
vars your new packs need. You can also run it yourself later.