create-starter-app · git:20260910.d17d88a · 2026-09-10 · sha256 f5cce8dbb02b3b77

create-starter-app git:20260910.d17d88aA

Immutable. This exact content is served forever at /api/v1/blob/f5cce8dbb02b3b77.

---
name: create-starter-app
description: Guide to create-starter-app (packages/create-starter-app), the interactive scaffolder that copies a starter template out of starter-templates/ — the arrow-key menu, how templates are resolved and copied, the package.json rewrite, and which template ids actually exist on disk. Use when working with create-starter-app or troubleshooting it — ENOENT when copying a template, the menu offering a template that isn't there, node_modules being copied, "directory already exists", or the CLI failing when installed from npm instead of run inside the monorepo.
---

# Working With create-starter-app

The scaffolder in `packages/create-starter-app`, published as **`create-starter-app`** (`bun create starter-app` / `npx create-starter-app`). It is deliberately tiny: pick a template from an arrow-key menu, name the project, and it copies a directory.

## Setup

```bash
bun create starter-app     # or: npx create-starter-app
```

Node ≥18, ESM. There are no flags or arguments — everything is prompted.

## How it resolves templates

`bin/create-starter-app.js` resolves `../../../starter-templates` relative to itself, i.e. **the `starter-templates/` directory at the root of this monorepo**. It then:

1. Shows the menu (↑↓ to move, Enter to confirm, `q` to quit).
2. Asks for a project name, defaulting to the template id minus the `template-` prefix.
3. Refuses to continue if that directory already exists.
4. `cpSync`s the template recursively, filtering out `node_modules`, `.next`, and `dist`.
5. Rewrites the copied `package.json`: sets `name` to the project name, sets `private: true`, deletes `version`.

Then it prints the next steps: `cd`, copy `.env.example`, install, `bun dev`.

## Menu entries vs. what exists on disk

The five entries in `TEMPLATES` do not all match directory names under `starter-templates/`:

| Menu id | On disk? |
| --- | --- |
| `template-nextjs-betterauth-shadcn-drizzle` | yes |
| `template-fumadocs` | yes |
| `template-docusaurus` | yes |
| `template-nextjs-betterauth-shadcn-prisma` | **no** — no such directory |
| `template-svelte-betterauth-drizzle-shadcn` | **no** — the directory is `template-svelte-betterauth-shadcn-drizzle` (word order differs) |

`starter-templates/template-vinext-betterauth-shadcn-themes-teams-stripe` exists but is **not** in the menu. Picking one of the two mismatched entries throws `ENOENT` from `cpSync`. Fixing it means editing the `id` fields in `TEMPLATES` (or renaming the directories) — the menu labels are decorative, only `id` is used for the path.

## Recipes

**Scaffold without the CLI** — since it's a plain recursive copy, this is equivalent:

```bash
cp -r starter-templates/template-fumadocs my-docs && cd my-docs
# then edit package.json name/version yourself
```

**Add a template** — create the directory under `starter-templates/`, then add an entry to `TEMPLATES` in `bin/create-starter-app.js` whose `id` is exactly the directory name.

## Troubleshooting

| Symptom | Cause → fix |
| --- | --- |
| `ENOENT … starter-templates/template-…` | The chosen menu entry's `id` doesn't match a real directory (see the table above). Pick a working template or correct the id. |
| Works in the repo, breaks when installed from npm | The path climbs three levels out of the package to reach the monorepo's `starter-templates/`, and `files` only publishes `bin` and `starters` (a directory that doesn't exist). From a global/npx install there's nothing to copy — run it from a checkout. |
| `Error: directory "x" already exists` | By design; it never merges into an existing directory. Choose another name or remove the old one. |
| Copied project contains `node_modules` | Only `node_modules`, `.next`, and `dist` are filtered, and only by substring match. Other build output (`.svelte-kit`, `.turbo`, `coverage`) comes along — delete it after copying. |
| Menu doesn't respond to arrow keys | It reads raw stdin; it needs a real TTY. It won't work through a pipe, in a non-interactive CI step, or inside some editor terminals. |
| `version` missing from the new `package.json` | Intentional — it's deleted so you set your own. `private: true` is also set to prevent accidental publishes. |
| The generated app won't start | Templates carry their own prerequisites (`.env.example` values, a database, a Cloudflare account). Read the template's own README before `bun dev`. |