CLI Reference
@saastro/forms ships a small CLI that runs through npx. It scaffolds
example forms and installs the shadcn/ui components those forms need.
npx @saastro/forms <command> [options]
Run with no arguments to print the inline help.
| Command / option | What it does |
|---|---|
init | Scaffold zero-config example forms |
install-deps | Install required shadcn components + peer deps, skipping ones that already exist |
--force | With install-deps: reinstall and overwrite components that already exist |
init
Scaffold two zero-config example forms in src/components/.
npx @saastro/forms init
init requires a components.json (the shadcn config) in the directory you
run it from — it exits with an error otherwise:
❌ No components.json found in the current directory.
Make sure you run this command from your project root and that shadcn/ui is initialized (npx shadcn@latest init).
If you don’t have one yet, run npx shadcn@latest init first.
Creates:
src/components/ExampleForm.tsx— programmatic form built withFormBuildersrc/components/ExampleFormJson.tsx— same form expressed as JSON config (useful as a starting point for storing forms in a database or CMS)
If either file already exists, it is left untouched and the CLI reports
exists <path> instead of create <path>.
After running init, you’ll be reminded to call:
npx @saastro/forms install-deps
This installs the shadcn components @saastro/forms needs (plus any missing
peer dependencies), skipping ones already in your project.
Use the generated form
import { ExampleForm } from '@/components/ExampleForm';
export default function ContactPage() {
return <ExampleForm />;
}
The example uses Vite’s import.meta.glob so every .tsx file in
@/components/ui/ is automatically wired into the form. Add new components
to that folder and they’ll be available without any registration step.
Vite only:
import.meta.globis a Vite feature — it does not exist in Next.js, webpack, or other bundlers. On non-Vite setups, replace the glob in the generated files with explicit imports passed via thecomponentsprop — see the Components guide.
install-deps
Read your project’s components.json (the shadcn config) and install every
shadcn component @saastro/forms field types use — the full set (input,
button, calendar, command, …), not just what the example forms reference —
skipping any that are already present.
npx @saastro/forms install-deps
What it does:
- Verifies you’re running it inside your own project (not inside the
@saastro/formspackage itself). - Reads
components.jsonto discover the project’s path aliases and component directory (see Alias resolution below). - Ensures
lib/utils.tsexists (the shadcncn()helper). Creates it if not. - Checks which components already exist in your
uidirectory and skips them, logging✓ <component> skipped (exists)for each one. Existing files are never overwritten unless you pass--force. - Runs
shadcn@latest add <missing-components> --yesto install whichever components are missing, then rewrites their imports to match the aliases configured in yourcomponents.json. - If the batch install fails (rare), retries one component at a time so a single broken package doesn’t block the rest.
When to run it:
- After the first
init. - Any time you add a field type whose required components you haven’t installed yet — the runtime fallback also tells you to run this command.
--force
By default, components that already exist in your ui directory are skipped
so your local customizations are safe. Pass --force to reinstall everything
and let shadcn overwrite the existing files:
npx @saastro/forms install-deps --force
When everything is already installed and you didn’t pass --force, the CLI
tells you so:
✅ All required components are already installed.
Pass --force to reinstall and overwrite them.
Alias resolution
The CLI reads the aliases field of your components.json (the shadcn
schema: aliases.ui, aliases.components, aliases.utils, …) and resolves
each alias to a real filesystem path:
- The alias root (e.g.
@in@/components/ui) is looked up in your tsconfig-style path mappings —compilerOptions.pathsintsconfig.json,tsconfig.app.json, orjsconfig.json(JSONC comments and trailing commas are handled). A mapping like"@/*": ["./app/*"]resolves@/components/uitoapp/components/ui. - If no mapping is found, the shadcn/Vite convention applies: the alias
root maps to
src(so@/components/ui→src/components/ui). - If
components.jsonhas no usablealiasesat all, the CLI falls back to the legacy@/→src/replacement and warns once:
⚠ components.json has no usable "aliases" entries. Falling back to the default "@/" → "src/" mapping.
Imports inside freshly installed components are rewritten to your configured
aliases (e.g. ../../lib/utils becomes whatever aliases.utils says —
@/lib/utils, ~/lib/utils, …).
What if I don’t have a components.json?
install-deps skips gracefully (exit code 0) instead of failing — it doubles
as a postinstall hook, so a monorepo root or server-only package shouldn’t
error out:
ℹ️ No components.json found. Skipping automatic component installation.
Initialize shadcn/ui first (npx shadcn@latest init), then run:
npx @saastro/forms install-deps
Run npx shadcn@latest init, answer the prompts, then re-run install-deps.
add (removed)
Earlier versions documented an add <name> command that copied custom
components bundled inside the package. The package bundles no custom
components, so the command could never succeed and has been removed. Running
it now prints a pointer to the right tools:
This package has no bundled components. Use `npx shadcn@latest add <name>` for shadcn components, or `npx @saastro/forms install-deps` to install everything the form needs.
To install a single shadcn component, use the shadcn CLI directly:
npx shadcn@latest add <component-name>
To install everything @saastro/forms needs, use install-deps.
Troubleshooting
The literal CLI output is quoted below so you can match it to what you see in your terminal.
”You are inside the @saastro/forms package itself.”
You ran install-deps from inside the @saastro/forms package source instead
of your own project. Switch to your project root (the directory where your
package.json lives) and run the command from there.
”No components.json found”
shadcn isn’t initialised in this project yet (this affects both init and
install-deps — they both read components.json). init exits with an
error; install-deps skips gracefully. Either way, run:
npx shadcn@latest init
…answer the prompts, then re-run npx @saastro/forms install-deps.
”Could not parse components.json”
Your components.json exists but isn’t valid JSON. Fix the syntax error
mentioned in the message and re-run the command.
A specific component fails to install
install-deps falls back to one-at-a-time installation when the batch run
fails (“⚠️ Batch install failed, retrying one component at a time…”). If a
particular component still fails, install it manually:
npx shadcn@latest add <component-name>
…and re-run install-deps to verify everything else is in place.
A component was skipped but I want a fresh copy
That’s the overwrite guard: ✓ <component> skipped (exists) means the file
is already in your ui directory and was left untouched. Re-run with
--force to overwrite it (this discards local edits to that file).
Peer dependencies (installed automatically since 0.2.1)
install-deps also verifies that the peer dependencies are declared in your
package.json and installs any that are missing:
zod— installs^4if missing (the published peer range also accepts^3.25if you already have it)react-hook-form(^7.67)react-day-picker(^9.11.3)date-fns(^4.1)
The check is by presence in your package.json, not by version — already
declared dependencies are never touched.
The package manager is detected from your lockfile in this priority order:
pnpm-lock.yaml→pnpm add ...bun.lockorbun.lockb→bun add ...yarn.lock→yarn add ...package-lock.json(or none) →npm install ...
react and react-dom are intentionally not auto-installed — every React
project already has them, and choosing between React 18 and 19 is a
project decision.