# Better-T-Stack

> A modern CLI for scaffolding end-to-end type-safe TypeScript projects with best practices and customizable configurations.

## When to use Better-T-Stack

Use Better-T-Stack when a developer or coding agent needs to scaffold a new TypeScript application, reproduce a selected stack from a command, add supported features to an existing Better-T-Stack project, inspect compatibility rules, or generate a project through a structured interface.

Better-T-Stack does not expose a public hosted application API. The supported automation interfaces are the CLI, its JSON commands, the programmatic npm API, and the local stdio MCP server.

## Quick start

```bash
npx create-better-t-stack@latest my-app
```

Non-interactive default project:

```bash
npx create-better-t-stack@latest my-app --yes
```

## Agent interfaces

- JSON project creation: `create-better-t-stack create-json --input '{...}'`
- JSON project updates: `create-better-t-stack add-json --input '{...}'`
- JSON schemas: `create-better-t-stack schema --name all`
- Local MCP server: `npx create-better-t-stack@latest mcp`
- [Agent workflow guide](https://www.better-t-stack.dev/docs/cli/agent-workflows.mdx)
- [Programmatic API](https://www.better-t-stack.dev/docs/cli/programmatic-api.mdx)

## Documentation

- [Quick Start](https://www.better-t-stack.dev/docs/index.mdx) — Create your first Better-T-Stack project in minutes
- [Analytics & Telemetry](https://www.better-t-stack.dev/docs/analytics.mdx) — What we collect, how to disable it, and where to view aggregated insights
- [bts.jsonc](https://www.better-t-stack.dev/docs/bts-config.mdx) — What bts.jsonc does and why it matters
- [Commands](https://www.better-t-stack.dev/docs/cli.mdx) — Complete reference for all CLI commands
- [Agent Workflows](https://www.better-t-stack.dev/docs/cli/agent-workflows.mdx) — JSON-first and schema-driven workflows for agents, scripts, and automation
- [Compatibility Rules](https://www.better-t-stack.dev/docs/cli/compatibility.mdx) — Understanding compatibility rules and restrictions between different CLI options
- [Options Reference](https://www.better-t-stack.dev/docs/cli/options.mdx) — Complete reference for all CLI options and flags
- [Programmatic API](https://www.better-t-stack.dev/docs/cli/programmatic-api.mdx) — Use Better-T-Stack programmatically in your Node.js applications
- [Using the interactive prompts](https://www.better-t-stack.dev/docs/cli/prompts.mdx) — How to navigate and answer the CLI's interactive questions
- [Contributing](https://www.better-t-stack.dev/docs/contributing.mdx) — How to set up your environment and contribute changes
- [Frequently Asked Questions](https://www.better-t-stack.dev/docs/faq.mdx) — Short answers to common beginner questions
- [Guides](https://www.better-t-stack.dev/docs/guides.mdx) — Practical guides for common setups
- [Deploying with Alchemy](https://www.better-t-stack.dev/docs/guides/cloudflare-alchemy.mdx) — Deploy Better-T-Stack projects to Cloudflare or Prisma and manage hosted databases in the same typed stack
- [Deploying with Docker Compose](https://www.better-t-stack.dev/docs/guides/docker.mdx) — Learn how to self-host your Better-T-Stack app with the generated Dockerfiles and docker-compose.yml
- [Deploying to Vercel](https://www.better-t-stack.dev/docs/guides/vercel.mdx) — Learn how to deploy your Better-T-Stack app to Vercel using Vercel Services
- [Project Structure](https://www.better-t-stack.dev/docs/project-structure.mdx) — Understanding the structure of projects created by Better-T-Stack CLI

## Project resources

- [Complete documentation in one Markdown file](https://www.better-t-stack.dev/llms-full.txt)
- [Browser stack builder](https://www.better-t-stack.dev/new)
- [npm package](https://www.npmjs.com/package/create-better-t-stack)
- [GitHub repository](https://github.com/AmanVarshney01/create-better-t-stack)
- [Sitemap](https://www.better-t-stack.dev/sitemap.xml)
- [Privacy notice](https://www.better-t-stack.dev/privacy)
- [Contact](https://www.better-t-stack.dev/contact)


---

# Analytics & Telemetry (/docs/analytics)



## What is collected [#what-is-collected]

On successful project creation, the CLI sends one anonymous event with:

* Selected options (stack choices), including: `frontend`, `backend`, `runtime`, `database`, `orm`, `api`, `auth`, `payments`, `addons`, `examples`, `dbSetup`, `webDeploy`, `serverDeploy`, `packageManager`, `git`, `install`
* Environment data: `cli_version`, `node_version`, `platform`
* How the CLI was driven (`mode`): interactive prompts, flags, `--yes`, JSON output, the programmatic API, or the MCP server

Not collected:

* Project name, path, or file contents (explicitly omitted)
* Nested addon or database provisioning options
* Secrets or environment variables from your machine
* IP addresses or persistent project/user identifiers

## Diagnostic events [#diagnostic-events]

Separately from the project-creation event, the CLI sends one anonymous `cli_failed` event to the same self-hosted Umami instance that powers the website analytics when `create` or `add` fails. It exists to catch problems the success event cannot show and contains: the command, how the CLI was driven, the stage that failed (for example `template-generation`, `file-writing`, `database-setup`, `addons-setup`, `dependency-installation`, `git-initialization`, `directory`, `validate`), the error class, the package manager and backend when known, and a scrubbed one-line reason with quoted names, paths, URLs, and emails replaced by placeholders. Cancelled runs send nothing.

Umami derives coarse location (country, region, city) from the request IP and a session hash from IP and user agent that rotates monthly; the IP address itself is not stored. The event never includes project names, paths, file contents, or full error messages. The same `BTS_TELEMETRY_DISABLED=1` or `DO_NOT_TRACK=1` switch and the `--disable-analytics` flag turn it off.

## Disable telemetry [#disable-telemetry]

Telemetry is enabled by default. To disable:

```bash
BTS_TELEMETRY_DISABLED=1 npx create-better-t-stack@latest
```

<Callout title="Note">
  The above command disables it for a single run.
</Callout>

Add `export BTS_TELEMETRY_DISABLED=1` to your shell profile to make it permanent.

The CLI also honors the cross-tool [`DO_NOT_TRACK=1`](https://consoledonottrack.com) convention, so one variable can turn off telemetry for every tool that supports it. The `--disable-analytics` flag skips it for a single run.

## Where to view analytics [#where-to-view-analytics]

* Charts: [`/analytics`](/analytics)
* Shared website analytics: [Umami dashboard](https://umami.amanv.cloud/share/pHvqHleyOl9PBfaK) (self-hosted on a Hostinger VPS). Besides page views, the website records anonymous interaction events: builder selections, presets, command copies, share actions, docs actions, and outbound link clicks. Event names and their properties are defined in `apps/web/src/lib/analytics.ts`.
* Aggregate JSON snapshot: `https://r2.better-t-stack.dev/analytics-data.json`
* Aggregate CSV export: `https://r2.better-t-stack.dev/export.csv`

Notes:

* Aggregates are updated atomically with each accepted event
* Incoming values are checked against a bounded schema before storage
* Raw data is not publicly exposed; the `/analytics` page presents only summary statistics

## Full transparency [#full-transparency]

One project event per successful scaffold plus one failure event when a scaffold breaks; no project identifiers. See source code below.

If in doubt, set `BTS_TELEMETRY_DISABLED=1` and proceed. You can still use all CLI features.

## Source code [#source-code]

* CLI event sender: [`apps/cli/src/utils/analytics.ts`](https://github.com/AmanVarshney01/create-better-t-stack/blob/main/apps/cli/src/utils/analytics.ts)
* CLI diagnostic events: [`apps/cli/src/utils/diagnostics.ts`](https://github.com/AmanVarshney01/create-better-t-stack/blob/main/apps/cli/src/utils/diagnostics.ts)
* Telemetry toggle logic: [`apps/cli/src/utils/telemetry.ts`](https://github.com/AmanVarshney01/create-better-t-stack/blob/main/apps/cli/src/utils/telemetry.ts)
* Ingest endpoint: [`packages/backend/convex/http.ts`](https://github.com/AmanVarshney01/create-better-t-stack/blob/main/packages/backend/convex/http.ts)
* Analytics backend (ingest + aggregation): [`packages/backend/convex/analytics.ts`](https://github.com/AmanVarshney01/create-better-t-stack/blob/main/packages/backend/convex/analytics.ts)


# bts.jsonc (/docs/bts-config)



## What is it? [#what-is-it]

`bts.jsonc` is a small config file written to your project root when you create a project. It captures the stack choices you selected (frontend, backend, API, DB/ORM, auth, addons, etc.). The file uses JSONC (JSON with comments) and includes a schema for editor hints.

Where: `./bts.jsonc`

## Why it exists [#why-it-exists]

* Required for the `add` command to detect your current stack
* Helps validate compatibility and pre‑fill sensible defaults

If `bts.jsonc` is missing, the `add` command cannot run because the project cannot be detected.

## Safe to delete (with a caveat) [#safe-to-delete-with-a-caveat]

It’s safe to delete for normal development; the generated code in `apps/*` and `packages/*` remains the source of truth. However, if you plan to use the `add` command later, you must keep `bts.jsonc` so the CLI can detect your project.

## Format [#format]

The file is JSONC with comments enabled and includes a `$schema` URL for tooling.

```jsonc
// Better-T-Stack project metadata
//
// Keep this file to use the `add` command.
// Add addons: bunx create-better-t-stack@latest add
//
// Docs: https://www.better-t-stack.dev/docs
// Stack Builder: https://www.better-t-stack.dev/new
{
  "$schema": "https://r2.better-t-stack.dev/schema.json",
  "version": "x.y.z",
  "createdAt": "2025-01-01T00:00:00.000Z",
  "reproducibleCommand": "bun create better-t-stack@latest my-app --frontend tanstack-router ...",
  "frontend": ["tanstack-router"],
  "backend": "hono",
  "runtime": "bun",
  "database": "sqlite",
  "orm": "drizzle",
  "api": "trpc",
  "auth": "better-auth",
  "payments": "none",
  "addons": ["turborepo"],
  "addonOptions": {
    "wxt": {
      "template": "react",
    },
  },
  "examples": [],
  "dbSetup": "none",
  "dbSetupOptions": {
    "mode": "manual",
  },
  "webDeploy": "none",
  "serverDeploy": "none",
  "packageManager": "bun",
}
```

Notes:

* Values mirror what you selected during project creation
* `reproducibleCommand` contains the normal CLI command generated when the project was created
* Deeply nested structured options remain in `addonOptions` or `dbSetupOptions` and may not be fully represented in the one-line command
* `addonOptions` stores nested configuration for prompt-driven addons
* `dbSetupOptions` stores structured database setup behavior for automation
* The file may be updated when you run `add` (addons and addon-specific config)

See also: [`add` command](/docs/cli#add)


# Contributing (/docs/contributing)



<Callout title="Important" type="warn">
  Before starting work on any new features or major changes, &#x2A;*please open an issue first to discuss
  your proposal and get approval.** We don't want you to **waste time** on work that might not align
  with the project's direction or get merged.
</Callout>

## Overview [#overview]

This project is a monorepo with two main apps:

* CLI: `apps/cli`
* Documentation site: `apps/web`

## Setup [#setup]

### Prerequisites [#prerequisites]

* Node.js (lts)
* Bun (recommended)
* Git

### Install [#install]

```bash
git clone https://github.com/AmanVarshney01/create-better-t-stack.git
cd create-better-t-stack
bun install
```

## Develop the CLI [#develop-the-cli]

```bash
cd apps/cli
# optional global link for testing anywhere
bun link
# run in watch mode (runs tsdown build in watch mode)
bun dev
```

Now go to anywhere else in your system (maybe like a test folder) and run:

```bash
create-better-t-stack
```

This will run the locally installed CLI.

## Develop the Docs [#develop-the-docs]

```bash
# from repo root
bun install
cd packages/backend
bun dev:setup  # you can choose local development too in prompts
```

Copy the Convex URL from `packages/backend/.env.local` to `apps/web/.env`:

```
NEXT_PUBLIC_CONVEX_URL=http://127.0.0.1:3210/
```

Now run `bun dev` in the root. It will complain about GitHub token, so run this in `packages/backend`:

```bash
npx convex env set GITHUB_ACCESS_TOKEN=xxxxx
npx convex env set GITHUB_WEBHOOK_SECRET=xxxxx
```

## Contribution Flow [#contribution-flow]

1. Open an issue/discussion before starting major work
2. Fork the repository
3. Create a feature branch
4. Make changes following existing code style
5. Update docs as needed
6. Test and format

```bash
# CLI
cd apps/cli && bun dev
cd apps/cli && bun run test

# Web
bun dev

# Lint + format checks
bun check
```

7. Commit and push

```bash
git add .
git commit -m "feat(web): ..." # or fix(cli): ...
git push origin <your-branch>
```

8. Open a Pull Request and link any related issues

## Commit Conventions [#commit-conventions]

Use conventional commit messages with the appropriate scope:

* `feat(cli): add new CLI feature`
* `fix(cli): fix CLI bug`
* `feat(web): add new web feature`
* `fix(web): fix web bug`
* `chore(web): update dependencies`
* `docs: update documentation`

## Help [#help]

* Issues and Discussions on GitHub
* Discord: [https://discord.gg/ZYsbjpDaM5](https://discord.gg/ZYsbjpDaM5)

See full contributor guide in the repository: `.github/CONTRIBUTING.md`.


# Frequently Asked Questions (/docs/faq)





## General [#general]

<Accordions type="single">
  <Accordion id="faq-1" title="What is Better‑T‑Stack?">
    An opinionated CLI that scaffolds full‑stack TypeScript projects (frontend, backend, API, DB/ORM, auth, addons) with a clean monorepo. See the Quick Start on the docs home.
  </Accordion>

  <Accordion id="faq-2" title="Do I need to install anything globally?">
    No. Run the CLI directly with your package manager. See Quick Start and the per‑command pages
    under CLI.
  </Accordion>

  <Accordion id="faq-3" title="Which package manager can I use?">
    `npm`, `pnpm`, or `bun` (all supported).
  </Accordion>

  <Accordion id="faq-4" title="What Node.js version is required?">
    Node.js LTS.
  </Accordion>

  <Accordion id="faq-5" title="Can I use this with an existing project?">
    The CLI is for new projects. You can migrate gradually or use `add` to extend a Better‑T‑Stack
    project.
  </Accordion>

  <Accordion id="faq-6" title="Where do generated files live?">
    See Project Structure for high‑level layouts (server‑based vs. Convex, optional web/native)..
  </Accordion>
</Accordions>

## Choosing options [#choosing-options]

<Accordions type="single">
  <Accordion id="faq-7" title="Does the CLI recommend a stack?">
    No. Pick what fits your needs. The CLI validates compatibility. See CLI (per command) and Compatibility for rules.
  </Accordion>

  <Accordion id="faq-8" title="I’m unsure between tRPC and oRPC / Drizzle and Prisma">
    See Compatibility for guidance and constraints. Both pairs work well; choose based on team and hosting needs.
  </Accordion>
</Accordions>

## Common issues [#common-issues]

<Accordions type="single">
  <Accordion id="faq-9" title="My mobile app can’t connect to the backend (Expo)">
    Set `EXPO_PUBLIC_SERVER_URL` in `apps/native/.env` to your machine IP (not `localhost`), check firewall, or try `npx expo start --tunnel`.
  </Accordion>

  <Accordion id="faq-10" title="How do I disable telemetry?">
    Set `BTS_TELEMETRY_DISABLED=1` (shell env). For one run, prefix the command; to make it permanent, export it in your shell profile. The cross-tool `DO_NOT_TRACK=1` convention is honored too.
  </Accordion>
</Accordions>

## Getting help [#getting-help]

* Docs: Quick Start, CLI, Project Structure, Compatibility
* Ask/Report: GitHub Issues & Discussions
* Community: Discord


# Quick Start (/docs)



## Philosophy [#philosophy]

* Roll your own stack: pick only what you need, nothing extra.
* Minimal templates: bare-bones scaffolds with zero bloat.
* Latest dependencies: always current and stable by default.
* Free and open source: forever.

## Get Started [#get-started]

### Prerequisites [#prerequisites]

* **Node.js LTS** - [Download from nodejs.org](https://nodejs.org/)
* **Git** (optional) - [Download from git-scm.com](https://git-scm.com/) - if you want to initialize a git repository
* **Bun** (optional) - [Download from bun.com](https://bun.com/) - if you want to use Bun as your package manager

### CLI (prompts) [#cli-prompts]

<CodeBlockTabs defaultValue="npm" groupId="package-manager">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npm create better-t-stack@latest
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm create better-t-stack
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn create better-t-stack
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bunx create-better-t-stack
    ```
  </CodeBlockTab>
</CodeBlockTabs>

Follow the interactive prompts to choose your frontend, backend, database, ORM, API layer, and addons.

Skip prompts and use the default stack:

<CodeBlockTabs defaultValue="npm" groupId="package-manager">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npm create better-t-stack@latest --yes
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm create better-t-stack --yes
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn create better-t-stack --yes
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bunx create-better-t-stack --yes
    ```
  </CodeBlockTab>
</CodeBlockTabs>

### Stack Builder (UI) [#stack-builder-ui]

* Visit [/new](/new) to pick your stack and copy the generated command
* Or open it via:

<CodeBlockTabs defaultValue="npm" groupId="package-manager">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npm create better-t-stack@latest builder
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm create better-t-stack builder
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn create better-t-stack builder
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bunx create-better-t-stack builder
    ```
  </CodeBlockTab>
</CodeBlockTabs>

### AI agent (plugin) [#ai-agent-plugin]

Let your AI assistant build the stack for you. Install the Better-T-Stack plugin, then describe what you want in plain English — it plans a valid stack and scaffolds it for you.

```bash
# Claude Code
/plugin marketplace add AmanVarshney01/create-better-t-stack
/plugin install better-t-stack@better-t-stack
```

Then ask: &#x2A;"create a fullstack app with Next, Hono, Postgres and Better Auth"*. Also works in Codex, opencode, and any MCP-capable agent — see [Use it inside your AI agent](/docs/cli/agent-workflows#use-it-inside-your-ai-agent).

## Common Setups [#common-setups]

### Default Stack [#default-stack]

<CodeBlockTabs defaultValue="npm" groupId="package-manager">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npm create better-t-stack@latest my-webapp \
      --frontend tanstack-router \
      --backend hono \
      --database sqlite \
      --orm drizzle \
      --auth better-auth \
      --addons turborepo
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm create better-t-stack my-webapp \
      --frontend tanstack-router \
      --backend hono \
      --database sqlite \
      --orm drizzle \
      --auth better-auth \
      --addons turborepo
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn create better-t-stack my-webapp \
      --frontend tanstack-router \
      --backend hono \
      --database sqlite \
      --orm drizzle \
      --auth better-auth \
      --addons turborepo
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bunx create-better-t-stack my-webapp \
      --frontend tanstack-router \
      --backend hono \
      --database sqlite \
      --orm drizzle \
      --auth better-auth \
      --addons turborepo
    ```
  </CodeBlockTab>
</CodeBlockTabs>

### Convex + React + Clerk [#convex--react--clerk]

<CodeBlockTabs defaultValue="npm" groupId="package-manager">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npm create better-t-stack@latest my-api \
      --frontend tanstack-router \
      --backend convex \
      --auth clerk
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm create better-t-stack my-api \
      --frontend tanstack-router \
      --backend convex \
      --auth clerk
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn create better-t-stack my-api \
      --frontend tanstack-router \
      --backend convex \
      --auth clerk
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bunx create-better-t-stack my-api \
      --frontend tanstack-router \
      --backend convex \
      --auth clerk
    ```
  </CodeBlockTab>
</CodeBlockTabs>

### Mobile App (Expo) [#mobile-app-expo]

<CodeBlockTabs defaultValue="npm" groupId="package-manager">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npm create better-t-stack@latest my-native \
      --frontend native-uniwind \
      --backend hono \
      --database sqlite \
      --orm drizzle \
      --auth better-auth
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm create better-t-stack my-native \
      --frontend native-uniwind \
      --backend hono \
      --database sqlite \
      --orm drizzle \
      --auth better-auth
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn create better-t-stack my-native \
      --frontend native-uniwind \
      --backend hono \
      --database sqlite \
      --orm drizzle \
      --auth better-auth
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bunx create-better-t-stack my-native \
      --frontend native-uniwind \
      --backend hono \
      --database sqlite \
      --orm drizzle \
      --auth better-auth
    ```
  </CodeBlockTab>
</CodeBlockTabs>

### Empty Monorepo [#empty-monorepo]

<CodeBlockTabs defaultValue="npm" groupId="package-manager">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npm create better-t-stack@latest my-workspace \
      --frontend none \
      --backend none
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm create better-t-stack my-workspace \
      --frontend none \
      --backend none
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn create better-t-stack my-workspace \
      --frontend none \
      --backend none
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bunx create-better-t-stack my-workspace \
      --frontend none \
      --backend none
    ```
  </CodeBlockTab>
</CodeBlockTabs>

## Flags Cheat Sheet [#flags-cheat-sheet]

See the full list in the [CLI Reference](/docs/cli). Key flags:

* `--frontend`: tanstack-router, react-router, tanstack-start, next, nuxt, svelte, solid, astro, native-bare, native-uniwind, native-unistyles, none
* `--backend`: hono, express, fastify, elysia, convex, self, none
* `--runtime`: bun, node, workers, none
* `--database`: sqlite, postgres, mysql, mongodb, none
* `--orm`: drizzle, prisma, mongoose, none
* `--api`: trpc, orpc, none
* `--auth`: better-auth, clerk, none
* `--payments`: polar, none
* `--addons`: turborepo, nx, vite-plus, pwa, tauri, electrobun, biome, lefthook, husky, starlight, fumadocs, ultracite, oxlint, mcp, opentui, wxt, skills, evlog, axiom, none
* `--examples`: todo, ai, none

## Next Steps [#next-steps]

<Cards>
  <Card href="/docs/cli" title="CLI (per-command)">
    Flags, usage, and examples for each command
  </Card>

  <Card href="/docs/cli/agent-workflows#use-it-inside-your-ai-agent" title="AI agent plugin">
    Scaffold from Claude Code, Codex, opencode, and other MCP agents
  </Card>

  <Card href="/docs/project-structure" title="Project Structure">
    See how web/server/native and Convex layouts are generated
  </Card>

  <Card href="/docs/cli/compatibility" title="Compatibility">
    Valid combinations for backend, runtime, database, ORM, API
  </Card>

  <Card href="/docs/bts-config" title="bts.jsonc">
    Required for the add command; safe to delete if you don't use add
  </Card>

  <Card href="/docs/contributing" title="Contributing">
    Dev setup and contribution flow
  </Card>
</Cards>


# Project Structure (/docs/project-structure)



## Overview [#overview]

Better-T-Stack CLI scaffolds a monorepo with `apps/*` and `packages/*`. `packages/config` is always present; other packages and apps appear based on your choices (frontend, backend, API, database/ORM, auth, addons, runtime, deploy). This page mirrors what the CLI actually writes.

## Root layout [#root-layout]

At the repository root you will see:

<Files>
  <Folder name="/">
    <Folder name="apps">
      <File name="..." />
    </Folder>

    <Folder name="packages">
      <File name="..." />
    </Folder>

    <File name="package.json" />

    <File name="bts.jsonc" />

    <File name="turbo.json" />

    <File name="nx.json" />

    <File name="pnpm-workspace.yaml" />

    <File name="bunfig.toml" />

    <File name=".npmrc" />

    <File name="README.md" />
  </Folder>
</Files>

Notes:

* `bts.jsonc` lets the CLI detect and enhance your project later; keep it if you plan to use `create-better-t-stack add`.
* `turbo.json` exists only if you picked the Turborepo addon.
* `nx.json` exists only if you picked the Nx addon.
* `pnpm-workspace.yaml`, `bunfig.toml`, and `.npmrc` are added based on the package manager you choose.
* `packages/infra` is created when a Cloudflare or Prisma deployment is enabled.

## Monorepo structure by backend [#monorepo-structure-by-backend]

### Server backends (hono, express, fastify, elysia) [#server-backends-hono-express-fastify-elysia]

<Files>
  <Folder name="my-app">
    <Folder name="apps">
      <Folder name="web">
        <File name="..." />
      </Folder>

      <Folder name="desktop">
        <File name="..." />
      </Folder>

      <Folder name="server">
        <File name="..." />
      </Folder>

      <Folder name="native">
        <File name="..." />
      </Folder>

      <Folder name="docs">
        <File name="..." />
      </Folder>
    </Folder>

    <Folder name="packages">
      <File name="..." />
    </Folder>
  </Folder>
</Files>

Notes:

* `apps/desktop` is created only with the Electrobun addon.
* `apps/docs` is created only with the Starlight addon.
* `apps/fumadocs` is created only with the Fumadocs addon.

### Self backend (fullstack) [#self-backend-fullstack]

When `--backend self` is used, API routes live inside `apps/web` (no `apps/server`).

<Files>
  <Folder name="my-app">
    <Folder name="apps">
      <Folder name="web">
        <File name="..." />
      </Folder>

      <Folder name="desktop">
        <File name="..." />
      </Folder>

      <Folder name="native">
        <File name="..." />
      </Folder>

      <Folder name="docs">
        <File name="..." />
      </Folder>
    </Folder>

    <Folder name="packages">
      <File name="..." />
    </Folder>
  </Folder>
</Files>

### Convex backend [#convex-backend]

<Files>
  <Folder name="my-app">
    <Folder name="apps">
      <Folder name="web">
        <File name="..." />
      </Folder>

      <Folder name="native">
        <File name="..." />
      </Folder>

      <Folder name="docs">
        <File name="..." />
      </Folder>
    </Folder>

    <Folder name="packages">
      <Folder name="backend">
        <File name="..." />
      </Folder>

      <Folder name="config">
        <File name="..." />
      </Folder>

      <Folder name="env">
        <File name="..." />
      </Folder>
    </Folder>
  </Folder>
</Files>

## Frontend Structure (apps/web) [#frontend-structure-appsweb]

The structure varies by framework. Items marked "(auth)" or "(API)" appear only when those options are enabled.

### React with TanStack Router [#react-with-tanstack-router]

<Files>
  <Folder name="apps/web">
    <Folder name="src">
      <Folder name="components">
        <File name="header.tsx" />

        <File name="loader.tsx" />

        <File name="mode-toggle.tsx" />

        <File name="theme-provider.tsx" />
      </Folder>

      <Folder name="routes">
        <File name="__root.tsx" />

        <File name="index.tsx" />
      </Folder>

      <Folder name="utils">
        <File name="trpc.ts" />
      </Folder>

      <File name="main.tsx" />

      <File name="index.css" />
    </Folder>

    <File name="index.html" />

    <File name="components.json" />

    <File name="tsconfig.json" />

    <File name="vite.config.ts" />

    <File name="package.json" />
  </Folder>

  <Folder name="packages/ui">
    <Folder name="src">
      <Folder name="components">
        <File name="button.tsx" />

        <File name="card.tsx" />

        <File name="checkbox.tsx" />

        <File name="dropdown-menu.tsx" />

        <File name="input.tsx" />

        <File name="label.tsx" />

        <File name="skeleton.tsx" />

        <File name="sonner.tsx" />
      </Folder>

      <Folder name="lib">
        <File name="utils.ts" />
      </Folder>

      <Folder name="styles">
        <File name="globals.css" />
      </Folder>
    </Folder>

    <File name="components.json" />

    <File name="package.json" />

    <File name="tsconfig.json" />
  </Folder>
</Files>

### Next.js [#nextjs]

<Files>
  <Folder name="apps/web">
    <Folder name="src">
      <Folder name="app">
        <File name="layout.tsx" />

        <File name="page.tsx" />
      </Folder>

      <Folder name="components">
        <File name="mode-toggle.tsx" />

        <File name="providers.tsx" />

        <File name="theme-provider.tsx" />
      </Folder>
    </Folder>

    <File name="components.json" />

    <File name="next.config.ts" />

    <File name="postcss.config.mjs" />

    <File name="tsconfig.json" />

    <File name="package.json" />
  </Folder>

  <Folder name="packages/ui">
    <Folder name="src">
      <Folder name="components">
        <File name="button.tsx" />

        <File name="card.tsx" />

        <File name="checkbox.tsx" />

        <File name="dropdown-menu.tsx" />

        <File name="input.tsx" />

        <File name="label.tsx" />

        <File name="skeleton.tsx" />

        <File name="sonner.tsx" />
      </Folder>

      <Folder name="lib">
        <File name="utils.ts" />
      </Folder>

      <Folder name="styles">
        <File name="globals.css" />
      </Folder>
    </Folder>

    <File name="components.json" />

    <File name="package.json" />

    <File name="tsconfig.json" />
  </Folder>
</Files>

Notes:

* If you choose `--backend self` with Next.js, TanStack Start, Nuxt, SvelteKit, Solid, or Astro, API routes live inside `apps/web` (no `apps/server`).
* (auth) adds `src/app/login/*` and `src/app/dashboard/*` plus sign-in components.

## React UI Customization [#react-ui-customization]

React web apps (`tanstack-router`, `react-router`, `tanstack-start`, and `next`) share shadcn/ui primitives through `packages/ui`.

* Change design tokens and global styles in `packages/ui/src/styles/globals.css`
* Update shared primitives in `packages/ui/src/components/*`
* Adjust shadcn aliases or style config in `packages/ui/components.json` and `apps/web/components.json`

### Add more shared components [#add-more-shared-components]

Run this from the project root to add more primitives to the shared UI package:

```bash
npx shadcn@latest add accordion dialog popover sheet table -c packages/ui
```

Import shared components like this:

```tsx
import { Button } from "@your-project/ui/components/button";
```

### Add app-specific blocks [#add-app-specific-blocks]

If you want to add app-specific shadcn blocks instead of shared primitives, run the shadcn CLI from `apps/web`.

## Backend Structure (apps/server) [#backend-structure-appsserver]

The server structure depends on your backend choice:

### Hono backend [#hono-backend]

<Files>
  <Folder name="apps/server">
    <Folder name="src">
      <Folder name="routers">
        <File name="index.ts" />
      </Folder>

      <File name="index.ts" />
    </Folder>

    <File name="package.json" />

    <File name="tsconfig.json" />
  </Folder>
</Files>

### Express / Fastify / Elysia [#express--fastify--elysia]

<Files>
  <Folder name="apps/server">
    <Folder name="src">
      <File name="index.ts" />
    </Folder>

    <File name="package.json" />

    <File name="tsconfig.json" />
  </Folder>
</Files>

### Workers runtime (optional) [#workers-runtime-optional]

When `runtime=workers`, the server targets Cloudflare Workers. If you also choose Cloudflare deployment, you'll get `packages/infra/alchemy.run.ts` and related infra files.

API and auth scaffolding (conditional):

* API=trpc: `src/lib/trpc.ts`, `src/lib/context.ts`
* API=orpc: `src/lib/orpc.ts`, `src/lib/context.ts`
* Auth: `src/lib/auth.ts`

## Database Configuration [#database-configuration]

Added only when you selected a database and ORM:

### Drizzle ORM [#drizzle-orm]

<Files>
  <Folder name="apps/server">
    <Folder name="src">
      <Folder name="db">
        <File name="index.ts" />
      </Folder>
    </Folder>

    <File name="drizzle.config.ts" />

    <Folder name="drizzle">
      <File name="..." />
    </Folder>
  </Folder>
</Files>

### Prisma ORM [#prisma-orm]

<Files>
  <Folder name="apps/server">
    <Folder name="prisma">
      <File name="schema.prisma" />

      <Folder name="migrations">
        <File name="..." />
      </Folder>
    </Folder>
  </Folder>
</Files>

### Mongoose (MongoDB) [#mongoose-mongodb]

<Files>
  <Folder name="apps/server">
    <Folder name="src">
      <Folder name="db">
        <File name="index.ts" />
      </Folder>
    </Folder>
  </Folder>
</Files>

### Auth + DB [#auth--db]

If you selected auth, additional files are added for your ORM:

* Drizzle: `src/db/schema/auth.ts`
* Prisma: `prisma/schema/auth.prisma`
* Mongoose: `src/db/models/auth.model.ts`

### Docker compose (optional) [#docker-compose-optional]

If `dbSetup=docker`, a `docker-compose.yml` is added in `apps/server/` for your database.

## Native App Structure (apps/native) [#native-app-structure-appsnative]

Created only when you include React Native (NativeWind or Unistyles):

<Files>
  <Folder name="apps/native">
    <Folder name="app">
      <Folder name="(tabs)">
        <File name="..." />
      </Folder>

      <File name="_layout.tsx" />

      <File name="index.tsx" />
    </Folder>

    <Folder name="components">
      <File name="..." />
    </Folder>

    <Folder name="lib">
      <File name="..." />
    </Folder>

    <Folder name="assets">
      <File name="..." />
    </Folder>

    <File name="app.json" />

    <File name="package.json" />

    <File name="tsconfig.json" />
  </Folder>
</Files>

If an API is selected, a client utility is added:

* API=trpc: `utils/trpc.ts`
* API=orpc: `utils/orpc.ts`

## Documentation Structure [#documentation-structure]

### Starlight (apps/docs) [#starlight-appsdocs]

<Files>
  <Folder name="apps/docs">
    <Folder name="src">
      <Folder name="content">
        <Folder name="docs">
          <File name="..." />
        </Folder>
      </Folder>

      <Folder name="pages">
        <File name="..." />
      </Folder>
    </Folder>

    <File name="astro.config.mjs" />

    <File name="package.json" />

    <File name="tsconfig.json" />
  </Folder>
</Files>

### Fumadocs (apps/fumadocs) [#fumadocs-appsfumadocs]

Fumadocs is generated by `create-fumadocs-app` inside `apps/fumadocs`. The exact structure depends on the template you choose (MDX vs static).

### Electrobun (apps/desktop) [#electrobun-appsdesktop]

Electrobun adds an `apps/desktop` workspace with its own `package.json`, `electrobun.config.ts`, and `src/bun/index.ts`. Electrobun 2 builds through Hutch: the first `electrobun` command downloads the toolchain and projects the SDK into `apps/desktop/.hutch/devkit` (gitignored), which the desktop `tsconfig.json` extends. The desktop shell reuses `apps/web` during development and bundles its built static output for release builds. Use a separate backend or no backend for desktop apps; `--backend self` emits server routes inside `apps/web` and cannot be bundled as static desktop assets.

## Configuration Files [#configuration-files]

### Better-T-Stack Config (bts.jsonc) [#better-t-stack-config-btsjsonc]

```json
{
  "$schema": "https://r2.better-t-stack.dev/schema.json",
  "version": "<cli-version>",
  "createdAt": "<timestamp>",
  "database": "<none|sqlite|postgres|mysql|mongodb>",
  "orm": "<none|drizzle|prisma|mongoose>",
  "backend": "<none|hono|express|fastify|elysia|convex|self>",
  "runtime": "<bun|node|workers|none>",
  "frontend": ["<tanstack-router|react-router|tanstack-start|next|nuxt|svelte|solid|astro|native-bare|native-uniwind|native-unistyles|none>"] ,
  "addons": ["<pwa|tauri|electrobun|starlight|fumadocs|biome|lefthook|husky|mcp|turborepo|nx|vite-plus|ultracite|oxlint|opentui|wxt|skills|evlog|axiom|none>"] ,
  "examples": ["<ai|todo|none>"] ,
  "auth": <"better-auth"|"clerk"|"none">,
  "packageManager": "<bun|pnpm|npm>",
  "dbSetup": "<turso|neon|prisma-postgres|planetscale|mongodb-atlas|supabase|d1|docker|none>",
  "api": "<none|trpc|orpc>",
  "webDeploy": "<cloudflare|docker|vercel|none>",
  "serverDeploy": "<cloudflare|docker|vercel|none>"
}
```

### Turborepo Config (turbo.json) [#turborepo-config-turbojson]

Generated only if you chose the Turborepo addon.

```json
{
  "$schema": "https://turbo.build/schema.json",
  "tasks": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": ["dist/**", ".next/**", "!.next/cache/**"]
    },
    "dev": {
      "cache": false,
      "persistent": true
    }
  }
}
```

### Nx Config (nx.json) [#nx-config-nxjson]

Generated only if you chose the Nx addon.

### Vite+ Config [#vite-config]

Generated Vite frontends import `defineConfig` from `vite-plus` when you choose the Vite+ addon. A root `vite.config.ts` centralizes Vite+ lint, format, and staged-file checks, while root workspace scripts use `vp run` for task orchestration.

When Vite+ is selected without Husky or Lefthook, generated projects also include `hooks:setup` for optional native Vite+ Git hooks. Run it if you want `vp config` to install hooks into `.vite-hooks` and use `vp staged` from `vite.config.ts`; see the [Vite+ commit hooks guide](https://viteplus.dev/guide/commit-hooks).

```ts
import { defineConfig } from "vite-plus";

export default defineConfig({
  lint: {
    options: {
      typeAware: false,
      typeCheck: false,
    },
  },
  fmt: {
    sortPackageJson: true,
  },
  staged: {
    "*.{js,ts,jsx,tsx,vue,svelte,json,jsonc,css,md}": "vp check --fix",
  },
});
```

## Shared packages [#shared-packages]

Better-T-Stack always creates `packages/config`. Other packages are added based on your selections:

* `packages/api`: when `--api` is not `none` (non-Convex)
* `packages/auth`: when `--auth` is not `none` (non-Convex)
* `packages/db`: when both `--database` and `--orm` are selected (non-Convex)
* `packages/backend`: Convex backend only
* `packages/infra`: Alchemy infrastructure for Cloudflare and/or Prisma deployments

## Environment configuration [#environment-configuration]

Each application owns an `.env.schema` and a generated `src/env.ts`. Varlock generates the local `ENV` accessor during installation. Run the root `env:generate` script after changing a schema; do not edit generated types. This uses `@generateTsTypes(path=./src/env.ts, exposeEnv=local)`, as recommended for separate application schemas in [Varlock's monorepo guide](https://varlock.dev/guides/monorepos/).

Shared database and authentication packages accept configuration and database clients from their owning app. Database command-line tools import the app's database settings through `packages/db/.env.schema`.

## Development Scripts [#development-scripts]

Scripts are adjusted based on your package manager and whether the Turborepo, Nx, or Vite+ addon is selected.

With Turborepo:

```json
{
  "scripts": {
    "dev": "turbo run dev",
    "build": "turbo run build",
    "check-types": "turbo run check-types",
    "dev:web": "turbo run dev -F web",
    "dev:server": "turbo run dev -F server",
    "db:push": "turbo run db:push -F server",
    "db:studio": "turbo run db:studio -F server"
  }
}
```

With Nx:

```json
{
  "scripts": {
    "dev": "nx run-many -t dev",
    "build": "nx run-many -t build",
    "check-types": "nx run-many -t check-types",
    "dev:web": "nx run-many -t dev --projects=web",
    "dev:server": "nx run-many -t dev --projects=server"
  }
}
```

With Vite+:

```json
{
  "scripts": {
    "dev": "vp run -r dev",
    "build": "vp run -r build",
    "check-types": "vp run -r check-types",
    "check": "vp check && vp run -r check-types",
    "lint": "vp lint",
    "format": "vp fmt",
    "staged": "vp staged",
    "dev:web": "vp run --filter web dev",
    "dev:server": "vp run --filter server dev"
  }
}
```

Without Turborepo, Nx, or Vite+ (example for Bun):

```json
{
  "scripts": {
    "dev": "bun run --filter '*' dev",
    "build": "bun run --filter '*' build",
    "check-types": "bun run --filter '*' check-types",
    "dev:web": "bun run --filter web dev",
    "dev:server": "bun run --filter server dev"
  }
}
```

Notes:

* Convex adds `dev:setup` for initial backend configuration.
* Database scripts (`db:*`) are added only when a database + ORM are selected (Drizzle/Prisma). D1 + Cloudflare omits `db:push` and `db:studio`.

## Key Details [#key-details]

* **Monorepo**: `apps/*` and `packages/*` are created only when relevant (except `packages/config`, which is always present)
* **React web base**: app-specific files stay in `apps/web`, while shared shadcn/ui primitives live in `packages/ui`
* **API clients**: `src/utils/trpc.ts` or `src/utils/orpc.ts` added to web/native when selected
* **Auth**: Adds authentication setup based on provider:
  * `better-auth`: `src/lib/auth.ts` on server and login/dashboard pages on web app
  * `clerk`: Clerk provider setup and authentication components
* **ORM/DB**: Drizzle/Prisma/Mongoose files added only when selected
* **Extras**: `pnpm-workspace.yaml`, `bunfig.toml`, or `.npmrc` added based on package manager and choices
* **Deploy**: Cloudflare or Prisma deployment adds one `packages/infra/alchemy.run.ts` containing the selected application and database resources

This reflects the actual files written by the CLI so new projects match what's documented here.


# Agent Workflows (/docs/cli/agent-workflows)



## Overview [#overview]

Better-T-Stack now supports a fully JSON-first workflow for agents and automation:

* Raw JSON project creation with `create-json`
* Raw JSON addon installation with `add-json`
* Runtime schema introspection with `schema`
* Local stdio MCP server with `mcp`
* Structured addon configuration with `addonOptions`
* Structured database setup configuration with `dbSetupOptions`
* Safe planning with `--dry-run`

If you are automating the CLI from an LLM, CI job, or another tool, start here instead of the interactive prompt flow.

## `create-json` [#create-json]

Create a project from a single JSON payload instead of many flags.

```bash
create-better-t-stack create-json --input '{
  "projectName": "my-app",
  "frontend": ["tanstack-router"],
  "backend": "hono",
  "runtime": "bun",
  "database": "postgres",
  "orm": "drizzle",
  "api": "trpc",
  "auth": "none",
  "addons": ["wxt", "mcp"],
  "addonOptions": {
    "wxt": { "template": "react", "devPort": 5555 },
    "mcp": {
      "scope": "project",
      "servers": ["context7"],
      "agents": ["cursor"]
    }
  },
  "install": false
}'
```

This is the best path when:

* Your input already exists as structured data
* You want to avoid shell flag expansion issues
* You need nested addon or database setup options

## `add-json` [#add-json]

Add addons to an existing project with a single JSON payload.

```bash
create-better-t-stack add-json --input '{
  "projectDir": "./my-app",
  "addons": ["skills", "ultracite"],
  "addonOptions": {
    "skills": {
      "scope": "project",
      "agents": ["cursor", "codex"],
      "selections": [
        {
          "source": "vercel-labs/agent-skills",
          "skills": ["web-design-guidelines"]
        },
        {
          "source": "https://www.evlog.dev",
          "skills": ["review-logging-patterns", "analyze-logs"]
        }
      ]
    },
    "ultracite": {
      "linter": "biome",
      "editors": ["vscode", "cursor"],
      "agents": ["claude", "codex"]
    }
  },
  "dryRun": true
}'
```

## Runtime Schema Introspection [#runtime-schema-introspection]

Use the CLI itself as the source of truth for current input shapes.

```bash
create-better-t-stack schema --name all
create-better-t-stack schema --name cli
create-better-t-stack schema --name createInput
create-better-t-stack schema --name addInput
create-better-t-stack schema --name addonOptions
create-better-t-stack schema --name dbSetupOptions
```

Useful patterns:

* `schema --name cli`: inspect available commands
* `schema --name createInput`: inspect the full create payload
* `schema --name addonOptions`: inspect nested addon configuration
* `schema --name dbSetupOptions`: inspect structured database setup behavior

## `mcp` [#mcp]

Run Better-T-Stack itself as a local MCP server over stdio:

```bash
npx create-better-t-stack@latest mcp
```

Install it into supported agent configs with `add-mcp`:

```bash
npx -y add-mcp@latest "npx -y create-better-t-stack@latest mcp"
```

Exposed tools:

* `bts_get_stack_guidance`
* `bts_get_schema`
* `bts_plan_project`
* `bts_create_project`
* `bts_plan_addons`
* `bts_add_addons`

This is the best path when an MCP-capable client should scaffold or modify Better-T-Stack projects without shelling out to a prompt-driven CLI flow.

Recommended MCP workflow:

1. Call `bts_get_stack_guidance` if the user's request is ambiguous.
2. Call `bts_get_schema` for the exact input shape you need.
3. Build a full explicit stack config.
4. Call `bts_plan_project` before `bts_create_project`.
5. For MCP execution, use `install: false` when creating projects.
6. Call `bts_plan_addons` before `bts_add_addons`.

For project creation, the MCP tools now expect a full explicit config, not a partial payload. That means the agent should provide all major stack choices such as:

* `frontend`
* `backend`
* `runtime`
* `database`
* `orm`
* `api`
* `auth`
* `payments`
* `addons`
* `examples`
* `dbSetup`
* `webDeploy`
* `serverDeploy`
* `git`
* `packageManager`
* `install`

Important field rules:

* `frontend` means app surfaces, not styling choices.
* `addons` must be an explicit array. Use `[]` when none are requested.
* `examples` must be an explicit array. Use `[]` when none are requested.
* `dbSetup`, `webDeploy`, and `serverDeploy` should be explicit even when the answer is `none`.
* `git`, `install`, and `packageManager` should always be set explicitly.
* `install` should usually be `false` for `bts_create_project`, because dependency installation can exceed common MCP client timeouts. Run `npm install`, `pnpm install`, or `bun install` separately after scaffolding.
* If a request is still ambiguous after reading the guidance, the agent should resolve that ambiguity before calling `bts_plan_project`.

If you scaffold a project with the `mcp` addon, Better-T-Stack itself is now one of the recommended MCP servers. The addon installs it through `add-mcp` with a package runner command so the generated config does not rely on a globally installed CLI:

```bash
npx -y add-mcp@latest "npx -y create-better-t-stack@latest mcp"
```

For Bun projects, the generated config uses the equivalent `bunx create-better-t-stack@latest mcp` server command inside `add-mcp`. That means MCP-capable agents inside the generated project can talk back to Better-T-Stack without any extra global install, alongside other recommended docs, framework, and database MCP servers.

## Use it inside your AI agent [#use-it-inside-your-ai-agent]

Install the Better-T-Stack plugin and your assistant will scaffold and extend projects for you — just ask in plain English, like &#x2A;"create a fullstack app with Next, Hono, Postgres and Better Auth"*, and it builds a valid stack instead of hand-writing boilerplate.

### Claude Code [#claude-code]

Add the marketplace and install the plugin:

```bash
/plugin marketplace add AmanVarshney01/create-better-t-stack
/plugin install better-t-stack@better-t-stack
```

Then ask for a project in chat, or use the shortcuts:

* `/better-t-stack:new <description>` — scaffold a new project
* `/better-t-stack:add <addons>` — add addons to an existing one

### Codex [#codex]

Add the same marketplace from the Codex plugins screen and install **Better-T-Stack**, then ask for a project in chat.

Prefer just the tools without the plugin? Wire the MCP server into Codex with `add-mcp`:

```bash
npx -y add-mcp@latest "npx -y create-better-t-stack@latest mcp"   # choose "codex"
```

Or add it to `~/.codex/config.toml` directly:

```toml
[mcp_servers.better-t-stack]
command = "npx"
args = ["-y", "create-better-t-stack@latest", "mcp"]
```

### opencode [#opencode]

Add the MCP server, then ask for a project in chat:

```bash
npx -y add-mcp@latest "npx -y create-better-t-stack@latest mcp"   # choose "opencode"
```

### Other agents [#other-agents]

Any other agent works too, because Better-T-Stack is a CLI underneath:

* **MCP-capable agents** (Cursor, VS Code, Gemini CLI, Zed, and more) — wire the server with `npx -y add-mcp@latest "npx -y create-better-t-stack@latest mcp"` and pick your agent.
* **CLI-first agents** like [pi](https://pi.dev) — just ask it to scaffold with Better-T-Stack and it runs the `create-better-t-stack` CLI directly.

## Structured Addon Options [#structured-addon-options]

Prompt-driven addons can be configured up front through `addonOptions`.

Supported structured addon surfaces include:

* `wxt`
* `fumadocs`
* `opentui`
* `mcp`
* `skills`
* `ultracite`

When `skills` is used with the `evlog` or `axiom` addon, Better-T-Stack recommends Evlog's agent skills from `https://www.evlog.dev`: `review-logging-patterns` and `analyze-logs`.

Example:

```json
{
  "addons": ["wxt"],
  "addonOptions": {
    "wxt": {
      "template": "react",
      "devPort": 5555
    }
  }
}
```

Structured addon options are still persisted in `bts.jsonc`, but the printed reproducible command now stays on normal CLI flags for consistency. That means deeply nested addon or provider-specific setup options are not fully encoded in the one-line replay command.

## Structured Database Setup Options [#structured-database-setup-options]

`dbSetupOptions` controls how database provisioning behaves in automation.

The supported modes are:

* `alchemy`: provision the selected provider through Alchemy
* `auto`: use the provider's automatic setup flow
* `manual`: use an existing database and configure its credentials yourself

Example:

```bash
create-better-t-stack create-json --input '{
  "projectName": "db-app",
  "database": "postgres",
  "orm": "drizzle",
  "backend": "hono",
  "runtime": "bun",
  "api": "trpc",
  "frontend": ["tanstack-router"],
  "dbSetup": "neon",
  "dbSetupOptions": {
    "mode": "manual"
  }
}'
```

Current behavior:

* When the database-consuming application deploys through Alchemy and the selected setup is Neon, PlanetScale, or Prisma Postgres, provisioning defaults to `alchemy`.

* Otherwise, providers with automatic cloud provisioning default to `manual` in silent/agent flows:
  * `turso`
  * `neon`
  * `prisma-postgres`
  * `supabase`
  * `mongodb-atlas`

* Providers that only write local/manual config today are not forced to `manual`:
  * `d1`
  * `docker`
  * `planetscale`

This keeps agents from accidentally creating cloud resources unless automation explicitly opts into `auto`, while leaving local or manual-only setups unchanged.

Provider-specific options are intentionally small today:

* `neon.method`, `neon.projectName`, `neon.regionId`
* `prismaPostgres.regionId`
* `turso.databaseName`, `turso.groupName`, `turso.installCli`

## Dry Runs [#dry-runs]

Use `--dry-run` to validate inputs and target directories without writing files.

```bash
create-better-t-stack --yes --dry-run
create-better-t-stack create-json --input '{"projectName":"my-app","yes":true,"dryRun":true}'
create-better-t-stack add-json --input '{"projectDir":"./my-app","addons":["mcp"],"dryRun":true}'
```

This is especially useful for:

* Planning actions before mutation
* CI validation
* Agent reasoning loops

## Programmatic API [#programmatic-api]

The exported `create()` and `add()` APIs run in silent mode by default, so they benefit from the same agent-safe behavior as the JSON commands.

See [Programmatic API](/docs/cli/programmatic-api) for TypeScript examples.

## Stored in `bts.jsonc` [#stored-in-btsjsonc]

Better-T-Stack persists structured configuration in `bts.jsonc`, including:

* `reproducibleCommand`
* `addonOptions`
* `dbSetupOptions`

See [`bts.jsonc`](/docs/bts-config) for the file format.


# Compatibility Rules (/docs/cli/compatibility)



## Overview [#overview]

The CLI validates option combinations to ensure generated projects work correctly. Here are the key compatibility rules and restrictions.

## Database & ORM Compatibility [#database--orm-compatibility]

### Required Combinations [#required-combinations]

| Database   | Compatible ORMs      | Notes                                     |
| ---------- | -------------------- | ----------------------------------------- |
| `sqlite`   | `drizzle`, `prisma`  | Lightweight, file-based database          |
| `postgres` | `drizzle`, `prisma`  | Advanced relational database              |
| `mysql`    | `drizzle`, `prisma`  | Traditional relational database           |
| `mongodb`  | `mongoose`, `prisma` | Document database, requires specific ORMs |
| `none`     | `none`               | No database setup                         |

### Restrictions [#restrictions]

* **MongoDB + Drizzle**: ❌ Not supported - Drizzle doesn't support MongoDB
* **Database without ORM**: ❌ Not supported - Database requires an ORM for code generation
* **ORM without Database**: ❌ Not supported - ORM requires a database target

```bash
# ❌ Invalid - MongoDB with Drizzle
create-better-t-stack --database mongodb --orm drizzle

# ✅ Valid - MongoDB with Mongoose
create-better-t-stack --database mongodb --orm mongoose
```

## Backend & Runtime Compatibility [#backend--runtime-compatibility]

### Cloudflare Workers Restrictions [#cloudflare-workers-restrictions]

Cloudflare Workers has specific compatibility requirements:

| Component      | Requirement                              | Reason                                                         |
| -------------- | ---------------------------------------- | -------------------------------------------------------------- |
| Backend        | Must be `hono`                           | Only Hono supports Workers runtime                             |
| ORM            | If DB is used, use `drizzle` or `prisma` | Mongoose is MongoDB-only and MongoDB is not workers-compatible |
| Database       | Cannot be `mongodb`                      | MongoDB is not compatible with Workers runtime                 |
| Database Setup | Cannot be `docker`                       | Workers is serverless, no Docker support                       |

```bash
# ❌ Invalid - Workers with Express
create-better-t-stack --runtime workers --backend express

# ✅ Valid - Workers with Hono
create-better-t-stack --runtime workers --backend hono --database sqlite --orm drizzle --db-setup d1

# ✅ Also valid - Workers with Prisma (D1)
create-better-t-stack --runtime workers --backend hono --database sqlite --orm prisma --db-setup d1
```

### Backend Presets [#backend-presets]

#### Convex Backend [#convex-backend]

When using `--backend convex`, these constraints apply:

* `--runtime none`
* `--database none` (Convex provides database)
* `--orm none` (Convex provides data layer)
* `--api none` (Convex provides API)
* `--db-setup none` (Convex manages hosting)
* `--server-deploy none`
* Auth can be `better-auth`, `clerk`, or `none` depending frontend compatibility

**Note:** Convex supports Clerk authentication with compatible frontends (React frameworks, Next.js, TanStack Start, and native frameworks). Nuxt, Svelte, Solid, and Astro are not compatible with Clerk.

**Better Auth with Convex:** supported frontends are `react-router`, `tanstack-router`, `tanstack-start`, `next`, `native-bare`, `native-uniwind`, and `native-unistyles`. Nuxt, Svelte, Solid, and Astro are not supported with Convex Better Auth.

#### No Backend [#no-backend]

When using `--backend none`, the following options are automatically set:

* `--auth none` (No backend for auth)
* `--database none` (No backend for database)
* `--orm none` (No database)
* `--api none` (No backend for API)
* `--runtime none` (No backend to run)
* `--db-setup none` (No database to host)
* `--examples none` (Examples require backend)

## Frontend & API Compatibility [#frontend--api-compatibility]

### API Framework Support [#api-framework-support]

| Frontend          | tRPC Support | oRPC Support | Notes                     |
| ----------------- | ------------ | ------------ | ------------------------- |
| `tanstack-router` | ✅            | ✅            | Full support              |
| `react-router`    | ✅            | ✅            | Full support              |
| `tanstack-start`  | ✅            | ✅            | Full support              |
| `next`            | ✅            | ✅            | Full support              |
| `nuxt`            | ❌            | ✅            | tRPC not supported        |
| `svelte`          | ❌            | ✅            | tRPC not supported        |
| `solid`           | ❌            | ✅            | Solid; tRPC not supported |
| `astro`           | ❌            | ✅            | tRPC not supported        |
| Native frameworks | ✅            | ✅            | Full support              |

```bash
# ❌ Invalid - Nuxt with tRPC
create-better-t-stack --frontend nuxt --api trpc

# ✅ Valid - Nuxt with oRPC
create-better-t-stack --frontend nuxt --api orpc
```

### Frontend Restrictions [#frontend-restrictions]

* **Multiple Web Frontends**: ❌ Only one web framework allowed
* **Multiple Native Frontends**: ❌ Only one native framework allowed
* **Web + Native**: ✅ One web and one native framework allowed

```bash
# ❌ Invalid - Multiple web frontends
create-better-t-stack --frontend next tanstack-router

# ✅ Valid - Web + native
create-better-t-stack --frontend next native-uniwind
```

## Database Setup Compatibility [#database-setup-compatibility]

### Provider Requirements [#provider-requirements]

| Setup Provider    | Required Database              | Notes                                                                                                            |
| ----------------- | ------------------------------ | ---------------------------------------------------------------------------------------------------------------- |
| `turso`           | `sqlite`                       | Distributed SQLite; works with Drizzle and Prisma                                                                |
| `d1`              | `sqlite`                       | Cloudflare D1; works with Drizzle and Prisma on Cloudflare Workers or supported self-hosted Cloudflare frontends |
| `neon`            | `postgres`                     | Serverless PostgreSQL                                                                                            |
| `supabase`        | `postgres`                     | PostgreSQL with additional features                                                                              |
| `prisma-postgres` | `postgres`                     | Managed PostgreSQL via Prisma                                                                                    |
| `planetscale`     | `mysql`, `postgres`            | PlanetScale serverless database                                                                                  |
| `mongodb-atlas`   | `mongodb`                      | Managed MongoDB                                                                                                  |
| `docker`          | `postgres`, `mysql`, `mongodb` | Not compatible with `sqlite` or Workers                                                                          |

### Special Cases [#special-cases]

#### Cloudflare D1 [#cloudflare-d1]

* Requires `--database sqlite`
* Requires one of these Cloudflare deployment targets:
  * `--backend hono --runtime workers --server-deploy cloudflare`
  * `--backend self --web-deploy cloudflare`
* With `--backend self`, D1 is supported on `next`, `tanstack-start`, `nuxt`, `svelte`, `solid`, and `astro`
* With `--backend self`, frontend API compatibility still applies: `nuxt`, `svelte`, `solid`, and `astro` require `--api orpc` or `--api none`

#### Docker Setup [#docker-setup]

* Cannot be used with `sqlite` (file-based database)
* Cannot be used with `workers` runtime (serverless environment)

## Addon Compatibility [#addon-compatibility]

### PWA Support [#pwa-support]

* Requires web frontend
* Compatible frontends: `tanstack-router`, `react-router`, `next`, `solid`
* Not compatible with native-only projects

The TanStack Router PWA caches its application shell for offline reloads. Solid,
React Router, and Next.js register a service worker and show a cached offline page
when a server-rendered page cannot be reached. Authenticated pages and API responses
are not cached. Test installation and offline behavior against a production build
served over HTTPS (or localhost), rather than relying on the development server.

### Tauri (Desktop Apps) [#tauri-desktop-apps]

* Requires web frontend
* Compatible frontends: `tanstack-router`, `react-router`, `tanstack-start`, `next`, `nuxt`, `svelte`, `astro`
* Desktop builds package static web output, so `tanstack-start`, `next`, `nuxt`, `svelte`, and `astro` need static/export configuration before packaging
* Not compatible with `--backend self`, because fullstack self backends emit server routes inside `apps/web`
* Cannot be combined with native frameworks

### Electrobun (Desktop Apps) [#electrobun-desktop-apps]

* Requires web frontend
* Compatible frontends: `tanstack-router`, `react-router`, `tanstack-start`, `next`, `nuxt`, `svelte`, `astro`
* Uses a generated `apps/desktop` shell that loads `apps/web` during development and bundles its static build output for distribution
* Desktop builds package static web output, so `tanstack-start`, `next`, `nuxt`, `svelte`, and `astro` need static/export configuration before packaging
* Not compatible with `--backend self`, because fullstack self backends emit server routes inside `apps/web`

### Task Runners [#task-runners]

* `nx`, `turborepo`, and `vite-plus` are mutually exclusive — only one can be selected per project

```bash
# ❌ Invalid - two task runners
create-better-t-stack --addons turborepo nx

# ✅ Valid - a single task runner
create-better-t-stack --addons turborepo
```

### Web Deployment [#web-deployment]

* `--web-deploy cloudflare`, `--web-deploy docker`, and `--web-deploy vercel` require a web frontend
* Cannot be used with native-only projects

### Server Deployment [#server-deployment]

* `--server-deploy cloudflare` requires `--runtime workers` with `--backend hono`
* `--server-deploy docker` and `--server-deploy vercel` require `--runtime bun` or `--runtime node`
* `--server-deploy` is not used for `--backend self`, because fullstack backends deploy with the web app

## Authentication Requirements [#authentication-requirements]

### Better-Auth Requirements [#better-auth-requirements]

Better-Auth authentication requires:

* A backend framework (cannot be `none`)
* With database: Requires an ORM
* Without database: Works with Convex backend or custom configuration
* With `--backend convex`: requires `react-router`, `tanstack-router`, `tanstack-start`, `next`, or a native Expo frontend

### Clerk Requirements [#clerk-requirements]

Clerk authentication requires:

* Compatible frontends (React frameworks, Next.js, TanStack Start, native frameworks)
* Supported backends: Convex, Hono, Express, Fastify, and Elysia
* Fullstack (`--backend self`) support with Next.js or TanStack Start
* Not compatible with Nuxt, Svelte, Solid, or Astro

### Payments Requirements [#payments-requirements]

Polar payments require:

* Better-Auth authentication
* A web frontend (or no frontend selected)

```bash
# ✅ Valid - Better-Auth without database
create-better-t-stack --auth better-auth --database none

# ✅ Valid - Better-Auth with full stack
create-better-t-stack --auth better-auth --database postgres --orm drizzle --backend hono

# ✅ Valid - Clerk with Hono
create-better-t-stack --auth clerk --backend hono --frontend tanstack-router

# ✅ Valid - Clerk with fullstack Next.js
create-better-t-stack --auth clerk --backend self --frontend next

# ❌ Invalid - Clerk with Astro
create-better-t-stack --auth clerk --backend self --frontend astro
```

## Example Compatibility [#example-compatibility]

### Todo Example [#todo-example]

* Requires a database when backend is present (except Convex)
* Requires an API layer (`trpc` or `orpc`) for non-Convex backends
* Cannot be used with `--backend none`

### AI Example [#ai-example]

* Not compatible with `--frontend solid`
* Not compatible with `--frontend astro`
* With `--backend convex`, Nuxt and Svelte frontends are not supported

## Common Error Messages [#common-error-messages]

### "Mongoose ORM requires MongoDB database" [#mongoose-orm-requires-mongodb-database]

```bash
# Fix by using MongoDB
create-better-t-stack --database mongodb --orm mongoose
```

### "Cloudflare Workers runtime is only supported with Hono backend" [#cloudflare-workers-runtime-is-only-supported-with-hono-backend]

```bash
# Fix by using Hono
create-better-t-stack --runtime workers --backend hono
```

### "Cannot select multiple web frameworks" [#cannot-select-multiple-web-frameworks]

```bash
# Fix by choosing one web framework
create-better-t-stack --frontend tanstack-router
```

### "Polar payments requires Better Auth" [#polar-payments-requires-better-auth]

```bash
# Fix by using Better-Auth
create-better-t-stack --payments polar --auth better-auth

# Or use Clerk without Polar payments
create-better-t-stack --auth clerk --backend hono --frontend tanstack-router
```

## Validation Strategy [#validation-strategy]

The CLI validates compatibility in this order:

1. **Basic validation**: Required parameters, valid enum values
2. **Combination validation**: Database + ORM, Backend + Runtime compatibility
3. **Feature validation**: Auth requirements, addon compatibility
4. **Example validation**: Example + stack compatibility

Understanding these rules helps you create valid configurations and troubleshoot issues when the CLI reports compatibility errors.


# Commands (/docs/cli)



## Overview [#overview]

The Better-T-Stack CLI provides several commands to manage your TypeScript projects.

## `create` (Default Command) [#create-default-command]

Creates a new Better-T-Stack project.

```bash
create-better-t-stack [project-directory] [options]
```

### Parameters [#parameters]

* `project-directory` (optional): Name or path for your project directory

### Key Options [#key-options]

* `--yes, -y`: Use default configuration (skips prompts)
* `--dry-run`: Validate configuration and target directory without writing files
* `--verbose`: Show detailed result information as JSON
* `--yolo`: Bypass validations and compatibility checks
* `--package-manager <pm>`: `npm`, `pnpm`, `bun`
* `--install / --no-install`: Install dependencies after creation
* `--open <target>`: Open the project in an installed editor, IDE, or coding agent
* `--git / --no-git`: Initialize Git repository
* `--frontend <types...>`: Web and/or native frameworks (see [Options](/docs/cli/options#frontend))
* `--backend <framework>`: `hono`, `express`, `fastify`, `elysia`, `convex`, `self`, `none`
* `--runtime <runtime>`: `bun`, `node`, `workers` (`none` only with `--backend convex`, `--backend none`, or `--backend self`)
* `--database <type>`: `none`, `sqlite`, `postgres`, `mysql`, `mongodb`
* `--orm <type>`: `none`, `drizzle`, `prisma`, `mongoose`
* `--api <type>`: `none`, `trpc`, `orpc`
* `--auth <provider>`: `better-auth`, `clerk`, `none` (see [Options](/docs/cli/options#authentication))
* `--payments <provider>`: `polar`, `none`
* `--db-setup <setup>`: `none`, `turso`, `d1`, `neon`, `supabase`, `prisma-postgres`, `planetscale`, `mongodb-atlas`, `docker`
* `--examples <types...>`: `none`, `todo`, `ai`
* `--web-deploy <setup>`: `none`, `cloudflare`
* `--server-deploy <setup>`: `none`, `cloudflare`
* `--template <type>`: `none`, `mern`, `pern`, `t3`, `uniwind`
* `--directory-conflict <strategy>`: `merge`, `overwrite`, `increment`, `error`
* `--render-title / --no-render-title`: Show/hide ASCII art title
* `--disable-analytics / --no-disable-analytics`: Control analytics collection
* `--manual-db`: Skip automatic database setup prompts

See the full reference in [Options](/docs/cli/options).

For JSON-first automation and agent usage, see [Agent Workflows](/docs/cli/agent-workflows).

### Examples [#examples]

```bash
# Default setup with prompts
create-better-t-stack

# Quick setup with defaults
create-better-t-stack --yes

# Specific configuration
create-better-t-stack --database postgres --backend hono --frontend tanstack-router

# Validate without writing files
create-better-t-stack my-app --yes --dry-run
```

## `add` [#add]

Adds addons or scaffolds a workspace package in an existing Better-T-Stack project.

```bash
create-better-t-stack add [options]
```

### Options [#options]

* `--addons <types...>`: Addons to add (see [Addons](/docs/cli/options#addons))
* `--package <name>`: Scaffold `packages/<name>` with a package manifest, shared TypeScript config, and `src/index.ts`
* `--project-dir <path>`: Project directory (defaults to current directory)
* `--install [boolean]`: Install dependencies after adding (defaults to `false`)
* `--package-manager <pm>`: Package manager to use (`npm`, `pnpm`, `bun`)
* `--dry-run`: Preview the changes without writing files
* `--disable-analytics`: Skip the anonymous failure event for this run (see [Analytics](/docs/analytics))

### Examples [#examples-1]

```bash
# Add addons interactively
create-better-t-stack add

# Add specific addons
create-better-t-stack add --addons pwa tauri --install

# Add addons in a specific project directory
create-better-t-stack add --project-dir ./my-app --addons mcp skills

# Scaffold @my-app/shared in packages/shared
create-better-t-stack add --package shared
```

## `create-json` [#create-json]

Create a project from a raw JSON payload.

```bash
create-better-t-stack create-json --input '{"projectName":"my-app","yes":true,"dryRun":true}'
```

Use this when you want a single machine-readable input object instead of many flags.

## `add-json` [#add-json]

Add addons from a raw JSON payload.

```bash
create-better-t-stack add-json --input '{"projectDir":"./my-app","addons":["wxt"],"addonOptions":{"wxt":{"template":"react"}}}'
```

## `schema` [#schema]

Print runtime CLI and input schemas as JSON.

```bash
create-better-t-stack schema --name all
create-better-t-stack schema --name createInput
create-better-t-stack schema --name addonOptions
create-better-t-stack schema --name dbSetupOptions
```

This is the best way for scripts and agents to discover the current input contract at runtime.

## `mcp` [#mcp]

Start the Better-T-Stack MCP server over stdio. It is launched by an MCP client (Claude Code, Codex, Cursor, opencode, and others), not run directly.

```bash
create-better-t-stack mcp
```

It exposes tools for planning and creating projects (`bts_plan_project`, `bts_create_project`) and managing addons (`bts_plan_addons`, `bts_add_addons`), plus schema and guidance helpers. See [Agent Workflows](/docs/cli/agent-workflows#use-it-inside-your-ai-agent) for setup and the full tool list.

## `sponsors` [#sponsors]

Displays Better-T-Stack sponsors.

```bash
create-better-t-stack sponsors
```

Shows a list of project sponsors and supporters.

## `docs` [#docs]

Opens the Better-T-Stack documentation in your default browser.

```bash
create-better-t-stack docs
```

Opens `https://www.better-t-stack.dev/docs` in your browser.

## `builder` [#builder]

Opens the web-based stack builder in your default browser.

```bash
create-better-t-stack builder
```

Opens `https://www.better-t-stack.dev/new` where you can configure your stack visually.

## `history` [#history]

Shows project creation history stored on your local machine.

```bash
create-better-t-stack history [options]
```

### Options [#options-1]

* `--limit <number>`: Number of entries to show (default: 10)
* `--json`: Output history as JSON
* `--clear`: Clear all history entries

### Examples [#examples-2]

```bash
# Show latest entries
create-better-t-stack history

# Show 5 entries
create-better-t-stack history --limit 5

# JSON output
create-better-t-stack history --json

# Clear history
create-better-t-stack history --clear
```

## Global Options [#global-options]

These options work with any command:

* `--help, -h`: Display help information
* `--version, -V`: Display CLI version

## Command Examples [#command-examples]

### Create a Full-Stack App [#create-a-full-stack-app]

```bash
create-better-t-stack \
  --database postgres \
  --orm drizzle \
  --backend hono \
  --frontend tanstack-router \
  --auth better-auth \
  --addons pwa biome
```

### Create a Backend-Only Project [#create-a-backend-only-project]

```bash
create-better-t-stack api-server \
  --frontend none \
  --backend hono \
  --database postgres \
  --orm drizzle \
  --api trpc
```

### Add Features to Existing Project [#add-features-to-existing-project]

```bash
cd my-existing-project
create-better-t-stack add --addons tauri starlight --install
```

### Show Local History [#show-local-history]

```bash
create-better-t-stack history --limit 20
```

## Programmatic Usage [#programmatic-usage]

For advanced use cases, automation, or integration with other tools, you can use the [Programmatic API](/docs/cli/programmatic-api) to create projects from Node.js code:

```typescript
import { create } from "create-better-t-stack";

const result = await create("my-app", {
  frontend: ["tanstack-router"],
  backend: "hono",
  database: "sqlite",
  orm: "drizzle",
});

result.match({
  ok: (data) => console.log(`Project created at: ${data.projectDirectory}`),
  err: (error) => console.error(`Failed: ${error.message}`),
});
```

This is useful for:

* **Build tools and generators** - Create projects from templates
* **CI/CD pipelines** - Generate test projects automatically
* **Development workflows** - Batch create related projects
* **Custom tooling** - Integrate with your existing development setup

See the [Programmatic API documentation](/docs/cli/programmatic-api) for complete examples and API reference.


# Options Reference (/docs/cli/options)



## General Options [#general-options]

### `--yes, -y` [#--yes--y]

Use default configuration and skip interactive prompts.

```bash
create-better-t-stack --yes
```

### `--template <type>` [#--template-type]

Use a predefined project template:

* `none`: No template (default)
* `mern`: MongoDB, Express, React, Node.js stack
* `pern`: PostgreSQL, Express, React, Node.js stack
* `t3`: T3 stack configuration
* `uniwind`: UniWind React Native template

```bash
create-better-t-stack --template t3
```

### `--manual-db` [#--manual-db]

Skip automatic database setup prompts and use manual database configuration.

```bash
create-better-t-stack --manual-db
```

### `--dry-run` [#--dry-run]

Validate configuration, compatibility, and directory handling without writing files.

```bash
create-better-t-stack my-app --yes --dry-run
```

### `--package-manager <pm>` [#--package-manager-pm]

Choose package manager: `npm`, `pnpm`, or `bun`.

```bash
create-better-t-stack --package-manager bun
```

### `--install / --no-install` [#--install----no-install]

Control dependency installation after project creation.

```bash
create-better-t-stack --no-install
```

### `--open <target>` [#--open-target]

Open the generated project in an installed editor, IDE, or coding agent. Interactive runs show an
installed-tools-only picker after scaffolding. Use `none` to skip it explicitly.

```bash
create-better-t-stack my-app --yes --open zed
create-better-t-stack my-app --yes --open codex
```

Supported targets include Cursor, VS Code, VS Code Insiders, Zed, Windsurf, VSCodium, WebStorm,
IntelliJ IDEA, Sublime Text, Neovim, the Codex app (macOS only), Codex CLI, Claude Code, OpenCode,
Pi, Gemini CLI, GitHub Copilot CLI, Kiro CLI, Factory Droid, goose, Cline CLI, Continue CLI, Amp,
Aider, Qwen Code, Crush, Cursor Agent, T3 Code, and Orca.

The picker is skipped for `--yes`, CI, and non-interactive terminals. An explicit `--open` value
still works in those modes. JSON and programmatic creation never launch external applications.

### `--git / --no-git` [#--git----no-git]

Control Git repository initialization.

```bash
create-better-t-stack --no-git
```

### `--yolo` [#--yolo]

Bypass validations and compatibility checks. Not recommended for normal use.

```bash
create-better-t-stack --yolo
```

### `--verbose` [#--verbose]

Show detailed result information in JSON format after project creation.

```bash
create-better-t-stack --verbose
```

### `--render-title / --no-render-title` [#--render-title----no-render-title]

Control whether the ASCII art title is shown. Enabled by default.

```bash
# Hide the title (useful in CI)
create-better-t-stack --no-render-title
```

### `--directory-conflict <strategy>` [#--directory-conflict-strategy]

How to handle existing, non-empty target directories:

* `merge`: Keep unrelated files and replace conflicting generated files
* `overwrite`: Permanently clear the directory before scaffolding
* `increment`: Create a suffixed directory (e.g., `my-app-1`)
* `error`: Fail instead of prompting

The interactive prompt recommends the first available suffixed directory and requires an extra
confirmation before overwriting. Project targets and generated paths that pass through symbolic
links are rejected instead of being followed.

```bash
# Overwrite an existing directory without prompting
create-better-t-stack my-app --yes --directory-conflict overwrite

# Safely create a new directory name if it exists
create-better-t-stack my-app --yes --directory-conflict increment
```

### `--disable-analytics / --no-disable-analytics` [#--disable-analytics----no-disable-analytics]

Control whether analytics and telemetry data is collected.

```bash
# Disable analytics collection
create-better-t-stack --disable-analytics

# Enable analytics collection (default)
create-better-t-stack --no-disable-analytics
```

Analytics help improve Better-T-Stack by providing insights into usage patterns. When disabled, no data is collected or transmitted.

For JSON-first automation, runtime schemas, and nested structured options, see [Agent Workflows](/docs/cli/agent-workflows).

## Database Options [#database-options]

### `--database <type>` [#--database-type]

Database type to use:

* `none`: No database
* `sqlite`: SQLite database
* `postgres`: PostgreSQL database
* `mysql`: MySQL database
* `mongodb`: MongoDB database

```bash
create-better-t-stack --database postgres
```

### `--orm <type>` [#--orm-type]

ORM to use with your database:

* `none`: No ORM
* `drizzle`: Drizzle ORM (TypeScript-first)
* `prisma`: Prisma ORM (feature-rich)
* `mongoose`: Mongoose ODM (for MongoDB)

```bash
create-better-t-stack --database postgres --orm drizzle
```

### `--db-setup <setup>` [#--db-setup-setup]

Database hosting/setup provider:

* `none`: Manual setup
* `turso`: Turso (SQLite)
* `d1`: Cloudflare D1 (SQLite; requires either Cloudflare Workers server deployment or `backend self` with Cloudflare web deployment)
* `neon`: Neon (PostgreSQL)
* `supabase`: Supabase (PostgreSQL)
* `prisma-postgres`: Prisma Postgres
* `planetscale`: PlanetScale (MySQL/PostgreSQL)
* `mongodb-atlas`: MongoDB Atlas
* `docker`: Local Docker containers

```bash
create-better-t-stack --database postgres --db-setup neon
```

If you need structured control over database provisioning behavior, use `dbSetupOptions` with `create-json` or the programmatic API. See [Agent Workflows](/docs/cli/agent-workflows).

For local Supabase development, install and start Docker or a compatible container runtime before choosing automatic setup.

If you choose manual setup, follow the commands printed by the CLI and add your database URL as `DATABASE_URL` and `DIRECT_URL` in your app's `.env` file. Run Supabase commands from `packages/db`. See Supabase's [local development guide](https://supabase.com/docs/guides/local-development/cli/getting-started) for managing your local database.

## Backend Options [#backend-options]

### `--backend <framework>` [#--backend-framework]

Backend framework to use:

* `none`: No backend
* `hono`: Hono (fast, lightweight)
* `express`: Express.js (popular, mature)
* `fastify`: Fastify (fast, plugin-based)
* `elysia`: Elysia (Bun-native)
* `convex`: Convex backend
* `self`: Self-hosted/custom backend

```bash
create-better-t-stack --backend hono
```

### `--runtime <runtime>` [#--runtime-runtime]

Runtime environment:

* `none`: No specific runtime (only with `convex`, `none`, or `self` backend)
* `bun`: Bun runtime
* `node`: Node.js runtime
* `workers`: Cloudflare Workers

```bash
create-better-t-stack --backend hono --runtime bun
```

### `--api <type>` [#--api-type]

API layer type:

* `none`: No API layer
* `trpc`: tRPC (type-safe)
* `orpc`: oRPC (OpenAPI-compatible)

```bash
create-better-t-stack --api trpc
```

## Frontend Options [#frontend-options]

### `--frontend <types...>` [#--frontend-types]

Frontend frameworks (can specify multiple):

**Web Frameworks:**

* `tanstack-router`: React with TanStack Router
* `react-router`: React with React Router
* `tanstack-start`: React with TanStack Start (SSR)
* `next`: Next.js
* `nuxt`: Nuxt (Vue)
* `svelte`: SvelteKit
* `solid`: Solid (SSR)
* `astro`: Astro

**Native Frameworks:**

* `native-bare`: React Native (bare setup)
* `native-uniwind`: React Native with UniWind (NativeWind alternative)
* `native-unistyles`: React Native with Unistyles

**No Frontend:**

* `none`: Backend-only project

```bash
# Single web frontend
create-better-t-stack --frontend tanstack-router

# Web + native frontend
create-better-t-stack --frontend next native-uniwind

# Backend-only
create-better-t-stack --frontend none
```

## Authentication [#authentication]

### `--auth <provider>` [#--auth-provider]

Choose authentication provider:

* `better-auth`: Better-Auth authentication (default)
* `clerk`: Clerk authentication
* `none`: No authentication

```bash
create-better-t-stack --auth better-auth
create-better-t-stack --auth clerk
create-better-t-stack --auth none
```

**Note:**

* `better-auth` requires a backend framework (cannot be `none`)
* if you choose a database, you should also choose an ORM
* with `--backend convex`, `better-auth` supports `react-router`, `tanstack-router`, `tanstack-start`, `next`, and native Expo frontends
* `clerk` requires a compatible frontend
* Supported Clerk backends: `convex`, `hono`, `express`, `fastify`, `elysia`, and `self` with Next.js or TanStack Start
* Authentication is automatically set to `none` when using `--backend none`

## Payments [#payments]

### `--payments <provider>` [#--payments-provider]

Payments provider:

* `none`: No payments integration
* `polar`: Polar payments integration

```bash
create-better-t-stack --payments polar --auth better-auth
```

**Note:** Polar payments requires Better-Auth authentication.

## Addons [#addons]

### `--addons <types...>` [#--addons-types]

Additional features to include:

* `none`: No addons
* `pwa`: Progressive Web App support
* `tauri`: Desktop app support for static web output (not compatible with `--backend self`)
* `electrobun`: Lightweight desktop shell for static web output (not compatible with `--backend self`)
* `starlight`: Starlight documentation site
* `fumadocs`: Fumadocs documentation site
* `biome`: Biome linting and formatting
* `lefthook`: Git hooks with Lefthook
* `husky`: Git hooks with Husky
* `turborepo`: Turborepo monorepo setup
* `nx`: Nx monorepo setup
* `vite-plus`: Vite+ unified toolchain, workspace task runner, linting, formatting, and optional native Git hooks
* `ultracite`: Ultracite configuration
* `oxlint`: Oxlint + Oxfmt (linting & formatting)
* `mcp`: Install MCP servers, including Better T Stack itself, with add-mcp
* `opentui`: OpenTUI components
* `wxt`: WXT browser extension framework
* `skills`: Install AI agent skills for coding assistants (Cursor, Claude Code, GitHub Copilot, etc.)
* `evlog`: Structured request logging for Hono, Express, Fastify, Elysia, or fullstack web backends
* `axiom`: Managed Axiom observability through Evlog and Alchemy (select during project creation)

```bash
create-better-t-stack --addons pwa biome husky
```

## Examples [#examples]

### `--examples <types...>` [#--examples-types]

Example implementations to include:

* `none`: No examples
* `todo`: Todo app example
* `ai`: AI chat interface example

```bash
create-better-t-stack --examples todo ai
```

## Deployment [#deployment]

### `--web-deploy <setup>` [#--web-deploy-setup]

Web deployment configuration:

* `none`: No deployment setup
* `cloudflare`: Cloudflare Workers deployment (via Alchemy infrastructure as code)
* `prisma`: Prisma deployment via Alchemy (all generated web frameworks)
* `docker`: Self-hosted deployment with a Dockerfile and a root `docker-compose.yml`
* `vercel`: Vercel Services deployment with a root `vercel.json`

```bash
create-better-t-stack --web-deploy docker
```

When the app that consumes Neon, PlanetScale, or Prisma Postgres is deployed with Alchemy, the CLI asks how the database should be provisioned and defaults to Alchemy. You can instead choose the provider's automatic setup when available or configure an existing database manually. With Alchemy selected, the generated stack provisions the database, injects runtime credentials, and applies checked-in migrations. Run `alchemy profile edit` from `packages/infra`; no separate provider setup step is required. See the [Alchemy deployment guide](/docs/guides/cloudflare-alchemy) for details.

## Automation Surfaces [#automation-surfaces]

These are part of the CLI contract for agents and scripts even though they are commands or JSON fields instead of traditional flags:

* `create-json`
* `add-json`
* `schema --name <schema>`
* `mcp`
* `addonOptions`
* `dbSetupOptions`

See [Agent Workflows](/docs/cli/agent-workflows) for examples.

### `--server-deploy <setup>` [#--server-deploy-setup]

Server deployment configuration:

* `none`: No deployment setup
* `cloudflare`: Cloudflare Workers deployment (when runtime is workers, via Alchemy infrastructure as code)
* `prisma`: Prisma deployment via Alchemy (requires the Bun or Node runtime)
* `docker`: Self-hosted deployment with a Dockerfile and a root `docker-compose.yml` (requires the bun or node runtime)
* `vercel`: Vercel Services deployment (requires the bun or node runtime)

```bash
create-better-t-stack --server-deploy docker
```

**Note:** The CLI displays this target simply as **Prisma**. The generated infrastructure uses Alchemy's Prisma provider. See the [Alchemy deployment guide](/docs/guides/cloudflare-alchemy) for details.

## History [#history]

### `history` [#history-1]

View your project creation history. Projects are tracked locally using platform-specific directories:

* **macOS**: `~/Library/Application Support/better-t-stack/history.json`
* **Linux**: `~/.local/share/better-t-stack/history.json`
* **Windows**: `%LOCALAPPDATA%\better-t-stack\Data\history.json`

```bash
# Show last 10 projects
create-better-t-stack history

# Show last 5 projects
create-better-t-stack history --limit 5

# Output as JSON
create-better-t-stack history --json

# Clear all history
create-better-t-stack history --clear
```

**Options:**

* `--limit <number>`: Number of entries to show (default: 10)
* `--clear`: Clear all project history
* `--json`: Output history as JSON

## Option Validation [#option-validation]

The CLI validates option combinations and will show errors for incompatible selections. See the [Compatibility](/docs/cli/compatibility) page for detailed rules.

## Examples [#examples-1]

### Full Configuration [#full-configuration]

```bash
create-better-t-stack \
  --database postgres \
  --orm drizzle \
  --backend hono \
  --runtime bun \
  --frontend tanstack-router \
  --api trpc \
  --auth better-auth \
  --addons pwa biome \
  --examples todo \
  --package-manager bun \
  --web-deploy cloudflare \
  --server-deploy cloudflare \
  --install
```

### Minimal Setup [#minimal-setup]

```bash
create-better-t-stack \
  --backend none \
  --frontend tanstack-router \
  --addons none \
  --examples none
```


# Programmatic API (/docs/cli/programmatic-api)



## Overview [#overview]

You can call Better-T-Stack directly from TypeScript/JavaScript without shelling out to the CLI.

The programmatic API is exported from `create-better-t-stack` and is designed for automation tools, internal generators, and scripted workflows.

Because it runs in silent mode by default, it also benefits from the same agent-safe behavior as `create-json`, including structured addon and database setup options.

## Installation [#installation]

<CodeBlockTabs defaultValue="npm" groupId="package-manager">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npm i create-better-t-stack
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm add create-better-t-stack
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn add create-better-t-stack
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bun add create-better-t-stack
    ```
  </CodeBlockTab>
</CodeBlockTabs>

## Quick Start [#quick-start]

```typescript
import { create } from "create-better-t-stack";

const result = await create("my-app", {
  frontend: ["tanstack-router"],
  backend: "hono",
  database: "sqlite",
  orm: "drizzle",
  auth: "better-auth",
  packageManager: "bun",
  install: false,
});

result.match({
  ok: (data) => {
    console.log(`Project created at: ${data.projectDirectory}`);
    console.log(`Reproducible command: ${data.reproducibleCommand}`);
  },
  err: (error) => {
    console.error(`Failed: ${error.message}`);
  },
});
```

## API Reference [#api-reference]

### `create(projectName?, options?)` [#createprojectname-options]

Create a new project.

```typescript
function create(
  projectName?: string,
  options?: Partial<CreateInput>,
): Promise<Result<InitResult, CreateError>>;
```

Notes:

* Uses the same option model as the CLI `create` command (`frontend`, `backend`, `database`, `orm`, `api`, `auth`, `addons`, etc.).
* Supports structured `addonOptions` and `dbSetupOptions`.
* Supports `dryRun` for validation-only automation.
* Validates JavaScript inputs at runtime using the same schemas as the CLI.
* Runs in silent mode (no interactive prompts / no CLI UI output).
* Returns a `Result` (`ok`/`err`) instead of exiting the process.

### `add(options?)` [#addoptions]

Add addons or scaffold a workspace package in an existing Better-T-Stack project.

```typescript
function add(options?: {
  addons?: Addons[];
  addonOptions?: AddonOptions;
  package?: string;
  install?: boolean;
  packageManager?: PackageManager;
  projectDir?: string;
  dryRun?: boolean;
}): Promise<AddResult>;
```

Example:

```typescript
import { add } from "create-better-t-stack";

const result = await add({
  projectDir: "./my-app",
  addons: ["biome", "mcp"],
  addonOptions: {
    mcp: {
      scope: "project",
      servers: ["context7"],
      agents: ["cursor"],
    },
  },
  install: true,
});

if (result.success) {
  console.log(`Added: ${result.addedAddons.join(", ")}`);
} else {
  console.error(result.error ?? "Failed to add addons");
}
```

`add()` validates its input at runtime and always returns an `AddResult`. Invalid input and
addon or package scaffolding failures return `{ success: false, error }` instead of throwing or exiting the process.

### `createVirtual(options)` [#createvirtualoptions]

Generate a project in memory without writing to disk.

```typescript
import { createVirtual } from "create-better-t-stack";

const result = await createVirtual({
  frontend: ["tanstack-router"],
  backend: "hono",
  database: "sqlite",
  orm: "drizzle",
  addonOptions: {
    wxt: {
      template: "react",
    },
  },
});

if (result.isErr()) {
  console.error(result.error.message);
}
```

This is useful for previews, tests, and web-based builders. Invalid input or incompatible project
configurations are returned as a `GeneratorError` with the `validation` phase.

### `sponsors()` [#sponsors]

Show sponsors (same behavior as CLI command).

### `docs()` [#docs]

Open docs URL (same behavior as CLI command).

### `builder()` [#builder]

Open the web stack builder (same behavior as CLI command).

## Result Types [#result-types]

### `InitResult` (from `create` on `ok`) [#initresult-from-create-on-ok]

```typescript
type InitResult = {
  success: boolean;
  projectConfig: ProjectConfig;
  reproducibleCommand: string;
  timeScaffolded: string;
  elapsedTimeMs: number;
  projectDirectory: string;
  relativePath: string;
  error?: string;
};
```

### `AddResult` (from `add`) [#addresult-from-add]

```typescript
type AddResult = {
  success: boolean;
  addedAddons: Addons[];
  projectDir: string;
  dryRun?: boolean;
  plannedFileCount?: number;
  addedPackage?: string;
  error?: string;
};
```

### `CreateError` [#createerror]

`create()` can return these error types in `Result.err(...)`:

* `UserCancelledError`
* `CLIError`
* `ProjectCreationError`

The `CreateInput`, `AddOptions`, `ProjectConfig`, `InitResult`, and `AddResult` types are exported
from `create-better-t-stack`.

## Error Handling Pattern [#error-handling-pattern]

```typescript
import { create } from "create-better-t-stack";

const result = await create("existing-dir", {
  directoryConflict: "error",
});

if (result.isErr()) {
  console.error(result.error.message);
  process.exit(1);
}

console.log(result.value.projectDirectory);
```

## Mapping CLI to Programmatic [#mapping-cli-to-programmatic]

CLI:

```bash
create-better-t-stack my-app \
  --frontend tanstack-router \
  --backend hono \
  --database postgres \
  --orm drizzle \
  --auth better-auth
```

Programmatic:

```typescript
const result = await create("my-app", {
  frontend: ["tanstack-router"],
  backend: "hono",
  database: "postgres",
  orm: "drizzle",
  auth: "better-auth",
  addonOptions: {
    wxt: { template: "react" },
  },
  dbSetupOptions: {
    mode: "manual",
  },
});
```

For the CLI-side JSON equivalents, see [Agent Workflows](/docs/cli/agent-workflows).


# Using the interactive prompts (/docs/cli/prompts)



## Overview [#overview]

The CLI uses `@clack/prompts` for interactive questions. These prompts work well in most terminals and are fully keyboard-driven.

## Core keys [#core-keys]

* **Navigate**: Up/Down arrow keys
* **Confirm/continue**: Enter
* **Cancel**: Ctrl+C

## Prompt types you’ll see [#prompt-types-youll-see]

### Single select (choose one) [#single-select-choose-one]

* **Move** with Up/Down, **Enter** to choose the highlighted option.

Typical places: choosing a web or native framework, picking a runtime or API.

### Multi-select (choose many) [#multi-select-choose-many]

* **Move** with Up/Down.
* **Space** toggles the highlighted option on/off.
* **Enter** confirms your selection(s).
* Some prompts allow selecting none (you can press Enter without toggling anything).

Typical places: selecting project types (web/native), choosing example apps.

### Grouped multi-select (addons) [#grouped-multi-select-addons]

* Options are organized under group headings.
* **Move** with Up/Down, **Space** to toggle an option, **Enter** to confirm.
* Group headings are informational; toggle the items within groups.

Used when selecting addons like Biome, PWA, Turborepo, etc.

### Confirm (yes/no) [#confirm-yesno]

* Use Left/Right or Up/Down to highlight Yes/No, then **Enter**.

Typical places: installing dependencies, initializing Git.

### Text input [#text-input]

* Type your answer and press **Enter**.
* If validation fails, a short message will explain what to fix; edit and press **Enter** again.

Typical places: project name/path, database URLs, provider-specific inputs.

## Tips [#tips]

* You can skip all prompts with `--yes` if you want the defaults. See the [Options](/docs/cli/options#yes--y) page.
* If you accidentally start the wrong flow, press **Ctrl+C** to cancel safely.


# Deploying with Alchemy (/docs/guides/cloudflare-alchemy)



## Overview [#overview]

[Alchemy](https://alchemy.run) is the infrastructure-as-code layer generated when you choose **Cloudflare** or **Prisma** as a deployment target. The CLI writes a `packages/infra` workspace containing:

* one `alchemy.run.ts` for applications, managed databases, and migrations
* root `dev`, `deploy`, and `destroy` commands

## Supported deployment shapes [#supported-deployment-shapes]

Cloudflare supports a separate Hono server on the Workers runtime and supported web frameworks. Full-stack `backend: self` projects deploy as one web application.

Prisma supports:

* web: TanStack Router, Next.js, Nuxt, Astro, React Router, TanStack Start, SvelteKit, and Solid
* server: Hono, Express, Fastify, and Elysia on Bun or Node
* mixed stacks, such as Cloudflare web + Prisma server or Prisma web + Cloudflare server

The CLI rejects unsupported framework/runtime combinations before writing files.

With Next.js on Cloudflare, Prisma ORM supports Neon and Prisma Postgres. Generic PostgreSQL connections, including Supabase and PlanetScale Postgres, are not supported for that combination.

```bash
# Full-stack Solid on Prisma with PlanetScale MySQL
bunx create-better-t-stack@latest my-app \
  --frontend solid \
  --backend self \
  --database mysql \
  --orm prisma \
  --db-setup planetscale \
  --web-deploy prisma

# Next.js on Cloudflare and a Hono/Bun server on Prisma
bunx create-better-t-stack@latest my-app \
  --frontend next \
  --backend hono \
  --runtime bun \
  --database postgres \
  --orm prisma \
  --db-setup neon \
  --web-deploy cloudflare \
  --server-deploy prisma
```

## Provider login [#provider-login]

Install dependencies, then let Alchemy configure every provider used by the stack:

```bash
bun install
cd packages/infra
bunx alchemy profile edit
```

Alchemy stores provider profiles under `~/.alchemy`. For Alchemy-managed databases, it creates the database and supplies the connection details to your application.

## Managed databases [#managed-databases]

Alchemy can create and manage Neon, PlanetScale, and Prisma Postgres databases when the backend deploys to Cloudflare or Prisma:

* `backend: self`: the web deployment owns the database
* separate backend: the server deployment owns the database

If the backend deploys elsewhere, configure its database separately.

| Setup                | What Alchemy creates                   |
| -------------------- | -------------------------------------- |
| Neon                 | A project and database connection      |
| PlanetScale Postgres | A database and application credentials |
| PlanetScale MySQL    | A database and application credentials |
| Prisma Postgres      | A project, database, and connection    |
| Cloudflare D1        | A database bound to your Worker        |

<Callout type="warn">
  PlanetScale can charge for `PS_DEV` databases. Review `clusterSize` in
  `packages/infra/alchemy.run.ts` before deploying.
</Callout>

## Migration workflow [#migration-workflow]

After changing your database schema, generate a migration:

```bash
# Drizzle: generate SQL from the current schema
bun run db:generate

# Prisma: create a migration, then regenerate the client
bun run db:migrate
bun run db:generate
```

Review and commit the generated migration files. Alchemy applies migrations for managed databases during deployment.

## Develop and deploy [#develop-and-deploy]

Run these commands from the project root:

```bash
bun run dev
bun run deploy
bun run destroy
```

* `dev` starts local development.
* `deploy` builds and deploys your applications and managed resources.
* `destroy` removes the resources in the selected stage, including managed databases.

Deployments are staged. For an explicit production stage:

```bash
cd packages/infra
bunx alchemy deploy --stage production
```

Use the same stage when destroying it:

```bash
bunx alchemy destroy --stage production
```

## Environment variables [#environment-variables]

Set infrastructure configuration in `packages/infra/.env` and application configuration in the relevant app's `.env` file. Alchemy supplies managed database connections and deployed application URLs automatically.

Keep secrets out of version control. Run `bun run deploy` after changing deployment configuration.

## Customize your infrastructure [#customize-your-infrastructure]

Edit `packages/infra/alchemy.run.ts` to change regions, database sizes, or add resources. See the [Alchemy documentation](https://alchemy.run) for the available resources and options.


# Deploying with Docker Compose (/docs/guides/docker)



## Overview [#overview]

This guide explains how Better-T-Stack packages your applications for self-hosting with [Docker Compose](https://docs.docker.com/compose/). You'll learn:

* What gets generated and how the compose stack is wired
* How to build, run, and monitor the containers
* How environment variables flow between builds, containers, and the compose file
* How the optional Docker database service works
* What to change when moving from local containers to a real host

## What Gets Generated [#what-gets-generated]

Choosing `docker` as a deploy target generates:

<Files>
  <Folder name="my-app">
    <File name="docker-compose.yml" />

    <File name=".dockerignore" />

    <Folder name="apps">
      <Folder name="web">
        <File name="Dockerfile" />

        <File name="nginx.conf" />
      </Folder>

      <Folder name="server">
        <File name="Dockerfile" />
      </Folder>
    </Folder>
  </Folder>
</Files>

* **`docker-compose.yml`** at the repo root defines the `web`, `server`, and (optionally) database services with health checks and startup ordering.
* **`apps/*/Dockerfile`** are multi-stage builds that install the whole monorepo workspace, build one app, and ship a minimal runtime image.
* **`nginx.conf`** is generated for the static TanStack Router SPA, which is served by nginx. Solid runs as a Node SSR server from its Nitro `.output` build.

## Enabling Docker Deployment [#enabling-docker-deployment]

**Combined deployment (web + server + database):**

<CodeBlockTabs defaultValue="npm" groupId="package-manager">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npm create better-t-stack@latest my-app \
      --frontend tanstack-router \
      --backend hono \
      --runtime bun \
      --database postgres \
      --db-setup docker \
      --web-deploy docker \
      --server-deploy docker
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm create better-t-stack my-app \
      --frontend tanstack-router \
      --backend hono \
      --runtime bun \
      --database postgres \
      --db-setup docker \
      --web-deploy docker \
      --server-deploy docker
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn create better-t-stack my-app \
      --frontend tanstack-router \
      --backend hono \
      --runtime bun \
      --database postgres \
      --db-setup docker \
      --web-deploy docker \
      --server-deploy docker
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bunx create-better-t-stack my-app \
      --frontend tanstack-router \
      --backend hono \
      --runtime bun \
      --database postgres \
      --db-setup docker \
      --web-deploy docker \
      --server-deploy docker
    ```
  </CodeBlockTab>
</CodeBlockTabs>

**Web-only (fullstack `self` backend):**

<CodeBlockTabs defaultValue="npm" groupId="package-manager">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npm create better-t-stack@latest my-app \
      --frontend next \
      --backend self \
      --web-deploy docker
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm create better-t-stack my-app \
      --frontend next \
      --backend self \
      --web-deploy docker
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn create better-t-stack my-app \
      --frontend next \
      --backend self \
      --web-deploy docker
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bunx create-better-t-stack my-app \
      --frontend next \
      --backend self \
      --web-deploy docker
    ```
  </CodeBlockTab>
</CodeBlockTabs>

**Server-only:**

<CodeBlockTabs defaultValue="npm" groupId="package-manager">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npm create better-t-stack@latest my-app \
      --frontend none \
      --backend fastify \
      --runtime node \
      --server-deploy docker
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm create better-t-stack my-app \
      --frontend none \
      --backend fastify \
      --runtime node \
      --server-deploy docker
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn create better-t-stack my-app \
      --frontend none \
      --backend fastify \
      --runtime node \
      --server-deploy docker
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bunx create-better-t-stack my-app \
      --frontend none \
      --backend fastify \
      --runtime node \
      --server-deploy docker
    ```
  </CodeBlockTab>
</CodeBlockTabs>

Server deployment requires the `bun` or `node` runtime. Adding `--db-setup docker` puts a Postgres, MySQL, or MongoDB container in the same compose file with a named volume and health check.

## Running the Stack [#running-the-stack]

```bash
bun docker:build   # docker compose build
bun docker:up      # docker compose up -d --build
bun docker:logs    # docker compose logs -f
bun docker:down    # docker compose down
```

After `docker:up`:

| Service  | URL                                 | Notes                                              |
| -------- | ----------------------------------- | -------------------------------------------------- |
| web      | `http://localhost:3001`             | nginx (SPA frontends) or the framework's server    |
| server   | `http://localhost:3000`             | health-checked; web waits for it to become healthy |
| database | `localhost:5432` / `3306` / `27017` | only with `--db-setup docker`                      |

Startup ordering is handled with `depends_on` + `condition: service_healthy`: the database must pass its health check before the server starts, and the server must pass its `fetch('http://localhost:3000/')` health check before the web container starts.

With `--db-setup docker` you also get scoped database scripts for local development without the full stack:

```bash
bun db:start   # docker compose up -d postgres   (just the database, detached)
bun db:watch   # docker compose up postgres      (with logs in the foreground)
```

## How the Images Build [#how-the-images-build]

Each Dockerfile is a multi-stage build with the monorepo root as build context:

1. A base stage installs the workspace with your package manager (with a mounted dependency cache, so rebuilds are fast).
2. Compose supplies the generated app env files as BuildKit secrets during installation and builds. Varlock validates the app schema, including database configuration needed by Prisma tooling. Public web variables also use build arguments. Fill required configuration before building; validation is not bypassed.
3. Runtime images retain the built output and the schema/Varlock CLI dependencies needed for startup. Next standalone preloads `varlock/auto-load` with Node. SPA frontends use nginx; SSR frontends and servers use Bun or Node images.

## Environment Variables [#environment-variables]

Three rules cover everything:

1. **Public web variables are baked at build time.** Values like `VITE_SERVER_URL` are compose **build args** — they're inlined into the client bundle when the image builds. The generated default is `http://localhost:3000` (the published server port). Changing them requires an image rebuild, not just a restart.
2. **Runtime variables come from each app's `.env` file.** Compose loads app values through `env_file`. The same files are required as build-secret sources when building images; Varlock rejects missing required values.
3. **Compose overrides handle container networking.** Inside the Docker network, containers reach each other by service name, not `localhost` — so compose sets values like `DATABASE_URL: postgresql://postgres:...@postgres:5432/my-app` and `CORS_ORIGIN: http://localhost:3001` in the `environment:` block, which wins over `env_file`. Your local `.env` files stay pointed at localhost for non-Docker development.

BuildKit keeps the mounted env files out of normal `COPY` layers. Frameworks that embed resolved server configuration, including Next, can still put those values in server build artifacts. See [Varlock encrypted deployments](https://varlock.dev/guides/encrypted-deployments/) for encrypting that configuration with a key supplied at both build and runtime.

Database passwords default to `password` and can be overridden with compose variables (`POSTGRES_PASSWORD`, `MYSQL_PASSWORD`, `MONGO_PASSWORD`) — set them in a root `.env` file or export them before `docker:up`.

## Moving to a Real Host [#moving-to-a-real-host]

The generated compose file is production-shaped (multi-stage images, health checks, `restart: unless-stopped`, named volumes), but a few things are sized for localhost:

* **Put a reverse proxy in front.** Terminate TLS with Caddy, Traefik, or nginx and route your domain to the web container; stop publishing the server and database ports publicly once the proxy handles routing.
* **Update baked URLs for your domain.** Rebuild the web image with the public server URL build arg set to your real API origin, and set `CORS_ORIGIN` / `BETTER_AUTH_URL` on the server to your real web origin.
* **Set real secrets.** Replace the default database password and put production values in the server's environment (compose `environment:`, an `.env` file on the host, or your orchestrator's secret store).
* **Back up the database volume.** Data lives in a named Docker volume (`<project>_postgres_data` etc.); `docker compose down -v` deletes it.

## Troubleshooting [#troubleshooting]

### Port already in use [#port-already-in-use]

The stack publishes 3001 (web), 3000 (server), and the database port. Stop whatever is bound to them, or edit the `ports:` mappings in `docker-compose.yml`.

### Changed a public env var but the web app didn't pick it up [#changed-a-public-env-var-but-the-web-app-didnt-pick-it-up]

Public variables are build args baked into the client bundle. Rebuild the image: `bun docker:up` (it passes `--build`) or `bun docker:build` first.

### Server can't reach the database with a localhost URL [#server-cant-reach-the-database-with-a-localhost-url]

Inside compose, the database host is the service name (`postgres`, `mysql`, `mongodb`), not `localhost`. Use the compose-provided `DATABASE_URL` (already set in the `environment:` block) instead of the one in `apps/server/.env`.

### Web container never starts [#web-container-never-starts]

It waits for the server health check. Check `bun docker:logs` — if the server is crash-looping (usually missing env vars or a bad `DATABASE_URL`), the web container stays in `waiting` state.

### CORS errors in the browser [#cors-errors-in-the-browser]

The compose file sets `CORS_ORIGIN: http://localhost:3001` when the web service is present. If you access the app from a different host or port, update that value to match the origin you're actually using and restart.


# Guides (/docs/guides)



## Guides [#guides]

Curated, task-focused guides for working with Better-T-Stack projects.

### Available Guides [#available-guides]

<Cards>
  <Card href="/docs/guides/cloudflare-alchemy" title="Deploying to Cloudflare with Alchemy">
    Deploy your app to Cloudflare Workers using Alchemy infrastructure-as-code. Covers web + server
    deployments, D1 databases, and environment management.
  </Card>
</Cards>

### Coming Soon [#coming-soon]

More guides are planned:

* Using Better-Auth with Convex
* Workers + D1 setup with Hono and Drizzle
* Adding PWA to React Router or Next.js
* Using Prisma with Neon or Supabase
* Monorepo tips with Turborepo

### Other Resources [#other-resources]

* [CLI Reference](/docs/cli) - Command options and usage
* [Compatibility](/docs/cli/compatibility) - Valid stack combinations
* [Project Structure](/docs/project-structure) - Understanding generated projects


# Deploying to Vercel (/docs/guides/vercel)



## Overview [#overview]

This guide explains how Better-T-Stack deploys your applications to [Vercel](https://vercel.com) using [Vercel Services](https://vercel.com/blog/vercel-services-run-full-stack-on-vercel). You'll learn:

* What Vercel Services are and how the generated `vercel.json` works
* How to deploy web apps, server apps, or both from one monorepo
* How environment variables are synced and derived
* How same-origin `/api` routing connects the web and server services
* How to verify a deployment before uploading anything

## What are Vercel Services? [#what-are-vercel-services]

**Vercel Services** let you run multiple apps — a frontend and one or more backends — as services inside a single Vercel project. Each service gets its own build and its own [Fluid Compute](https://vercel.com/docs/fluid-compute) functions, while sharing one domain, one set of environment variables, and one deployment lifecycle.

Better-T-Stack uses this to deploy your monorepo's `apps/web` and `apps/server` together: the web service serves your frontend, the server service runs your Hono/Express/Fastify/Elysia backend, and rewrites route `/api/*` requests between them on the same domain. No CORS configuration, no separate projects to keep in sync.

## Enabling Vercel Deployment [#enabling-vercel-deployment]

When creating a project:

**Combined deployment (web + server):**

<CodeBlockTabs defaultValue="npm" groupId="package-manager">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npm create better-t-stack@latest my-app \
      --frontend tanstack-router \
      --backend hono \
      --runtime node \
      --web-deploy vercel \
      --server-deploy vercel
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm create better-t-stack my-app \
      --frontend tanstack-router \
      --backend hono \
      --runtime node \
      --web-deploy vercel \
      --server-deploy vercel
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn create better-t-stack my-app \
      --frontend tanstack-router \
      --backend hono \
      --runtime node \
      --web-deploy vercel \
      --server-deploy vercel
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bunx create-better-t-stack my-app \
      --frontend tanstack-router \
      --backend hono \
      --runtime node \
      --web-deploy vercel \
      --server-deploy vercel
    ```
  </CodeBlockTab>
</CodeBlockTabs>

**Web-only (fullstack `self` backend):**

<CodeBlockTabs defaultValue="npm" groupId="package-manager">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npm create better-t-stack@latest my-app \
      --frontend next \
      --backend self \
      --web-deploy vercel
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm create better-t-stack my-app \
      --frontend next \
      --backend self \
      --web-deploy vercel
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn create better-t-stack my-app \
      --frontend next \
      --backend self \
      --web-deploy vercel
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bunx create-better-t-stack my-app \
      --frontend next \
      --backend self \
      --web-deploy vercel
    ```
  </CodeBlockTab>
</CodeBlockTabs>

**Server-only:**

<CodeBlockTabs defaultValue="npm" groupId="package-manager">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="npm">
      npm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="pnpm">
      pnpm
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="yarn">
      yarn
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="bun">
      bun
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="npm">
    ```bash
    npm create better-t-stack@latest my-app \
      --frontend none \
      --backend hono \
      --runtime node \
      --server-deploy vercel
    ```
  </CodeBlockTab>

  <CodeBlockTab value="pnpm">
    ```bash
    pnpm create better-t-stack my-app \
      --frontend none \
      --backend hono \
      --runtime node \
      --server-deploy vercel
    ```
  </CodeBlockTab>

  <CodeBlockTab value="yarn">
    ```bash
    yarn create better-t-stack my-app \
      --frontend none \
      --backend hono \
      --runtime node \
      --server-deploy vercel
    ```
  </CodeBlockTab>

  <CodeBlockTab value="bun">
    ```bash
    bunx create-better-t-stack my-app \
      --frontend none \
      --backend hono \
      --runtime node \
      --server-deploy vercel
    ```
  </CodeBlockTab>
</CodeBlockTabs>

Server deployment requires the `node` runtime and a separate backend (hono, express, fastify, elysia). Bun remains fully supported as the package manager. Vercel's Bun runtime is not an option for the server because `varlock/auto-load` launches the Varlock CLI, which needs Node. For the Cloudflare `workers` runtime, use `--server-deploy cloudflare` instead.

## Understanding vercel.json [#understanding-verceljson]

The generated `vercel.json` defines your services and routing. A combined web + server deployment looks like this:

```json
{
  "$schema": "https://openapi.vercel.sh/vercel.json",
  "services": {
    "web": {
      "root": "apps/web",
      "framework": "vite",
      "installCommand": "cd ../.. && bun install",
      "buildCommand": "VITE_SERVER_URL=/api bun run build",
      "rewrites": [{ "source": "/(.*)", "destination": "/index.html" }]
    },
    "server": {
      "root": "apps/server",
      "framework": "hono",
      "entrypoint": "src/index.ts",
      "installCommand": "cd ../.. && bun install",
      "buildCommand": "bun run env:generate && bun run check-types",
      "functions": {
        "src/index.ts": {
          "includeFiles": "{package.json,apps/server/.env.schema,node_modules/.bin/varlock,node_modules/varlock/**}"
        }
      }
    }
  },
  "rewrites": [
    { "source": "/api/(.*)", "destination": { "service": "server" } },
    { "source": "/(.*)", "destination": { "service": "web" } }
  ]
}
```

How the pieces fit together:

* **Rewrites** send `/api/*` to the server service and everything else to the web service. Order matters — the more specific rule wins.
* **Backend routes** match the public paths directly: `/api/trpc`, `/api/rpc`, and `/api/auth`. [Vercel Services pass the original request path](https://vercel.com/docs/services/routing) to the service, so the server mounts its handlers under `/api` for this setup. Local server URLs include `/api` too, so development and production match.
* **The web buildCommand** inlines your public server URL as `/api`, so the client calls the same domain it was served from. The generated oRPC/tRPC clients normalize this relative path to an absolute URL at runtime (browsers use `window.location.origin`; SSR uses the deployment's own URL).
* **The server buildCommand** generates environment types and type-checks the server. Vercel compiles the TypeScript entrypoint itself and traces its dependencies.
* **includeFiles** packages the server schema and the Varlock CLI with the function. `varlock/auto-load` runs the CLI at startup, which file tracing can't follow. The function runs from the repository root, so the generated root `package.json` sets `varlock.loadPath` to `./apps/server/`.
* **Prisma projects** run `db:generate` before each service build, because Prisma Client is generated code.
* **installCommand** runs the package manager from the monorepo root so workspace dependencies resolve.

### Framework Support [#framework-support]

| Frontend        | Service framework | Backend | Notes                                    |
| --------------- | ----------------- | ------- | ---------------------------------------- |
| Next.js         | `nextjs`          | hono    | Entrypoint exports the app for functions |
| Nuxt            | `nuxtjs`          | express | Runs unmodified (listen is intercepted)  |
| SvelteKit       | `sveltekit`       | fastify | Runs unmodified (listen is intercepted)  |
| Astro           | `astro`           | elysia  | Entrypoint exports the app for functions |
| React Router    | `react-router`    |         | SSR via the framework preset             |
| TanStack Start  | `tanstack-start`  |         |                                          |
| TanStack Router | `vite`            |         |                                          |
| Solid           | `nitro`           |         | SSR via Nitro's Vercel preset            |

Astro projects automatically use the [`@astrojs/vercel`](https://docs.astro.build/en/guides/integrations-guide/vercel/) adapter instead of the Node standalone adapter.

### Choosing a region [#choosing-a-region]

Functions deploy to Washington, D.C. (`iad1`) by default. If your database lives elsewhere, every query pays a cross-region round trip — an SSR page making a few queries can spend hundreds of milliseconds on network alone. Set the region where your database lives (static assets are served from Vercel's CDN close to users either way):

```json
{
  "regions": ["sin1"]
}
```

`regions` is top-level in `vercel.json` and applies to all services. Common IDs: `iad1` (Washington), `sfo1` (San Francisco), `fra1` (Frankfurt), `sin1` (Singapore), `bom1` (Mumbai), `syd1` (Sydney) — [full list](https://vercel.com/docs/regions#region-list). Pick the same region you chose during database setup (Neon, Prisma Postgres, Turso, etc.).

## Deploying [#deploying]

The full flow from a fresh scaffold to production:

```bash
cd my-app

# 1. Authenticate (first time only)
bunx vercel login

# 2. Link the repo to a Vercel project (creates .vercel/project.json)
bun deploy:setup

# 3. (Optional) Dry-run: preview framework detection and files without uploading
bun deploy:check

# 4. Sync envs for the environment you deploy to, then deploy.
#    Preview and production are separate env stores on Vercel.
bun env:preview && bun deploy            # preview
bun env:production && bun deploy:prod    # production
```

<Callout type="warn">
  Sync your environment variables **before** the first deploy to each environment — preview and
  production are separate stores. Vercel never uploads local `.env` files, so a deploy without
  synced envs fails environment validation at runtime.
</Callout>

<Callout type="info">
  If your Vercel account belongs to multiple teams, `vercel link --yes` needs an explicit scope:
  `bunx vercel link --yes --scope your-team`.
</Callout>

## Environment Variables [#environment-variables]

There are three layers. Local `.env` files stay the source of truth, and one command projects them into Vercel.

### 1. Local .env files [#1-local-env-files]

Each app owns its `.env.schema`, env value files, and generated `src/env.ts` accessor. Varlock validates configuration through the framework integration or server bootstrap. Mark browser values `@public`; keep secrets in server configuration.

### 2. Syncing to Vercel [#2-syncing-to-vercel]

```bash
bun env:preview      # sync to the preview environment
bun env:production   # sync to the production environment
```

The generated `scripts/sync-vercel-env.ts` parses your `.env` files and pushes each key with the Vercel CLI. Because web and server are services in **one project, they share one environment store** — a single sync covers both. Extra CLI flags pass straight through:

```bash
bun env:production --scope your-team
```

When the app that owns these keys runs on Vercel (combined web + server deployments, or a fullstack `self` backend), the sync is not a blind copy:

| Key                                                                                           | Behavior             | Why                                                            |
| --------------------------------------------------------------------------------------------- | -------------------- | -------------------------------------------------------------- |
| `VITE_SERVER_URL` / `NEXT_PUBLIC_SERVER_URL` / `PUBLIC_SERVER_URL` / `NUXT_PUBLIC_SERVER_URL` | Overridden to `/api` | The browser must call the same origin; rewrites do the routing |
| `CORS_ORIGIN`, `BETTER_AUTH_URL`, `NODE_ENV`                                                  | Skipped              | Derived at runtime from the deployment URL (see below)         |
| Everything else (`DATABASE_URL`, secrets, …)                                                  | Copied as-is         |                                                                |

### 3. Runtime derivation [#3-runtime-derivation]

The generated server env derives `CORS_ORIGIN` and `BETTER_AUTH_URL` from Vercel's own variables when they aren't set: production deployments use `VERCEL_PROJECT_PRODUCTION_URL`, previews use `VERCEL_URL`. This is why the sync skips them — every deployment, including previews, self-configures with the right origin and zero per-environment setup.

<Callout type="info">
  Synced values are stored as **Sensitive** by default, which means `vercel env pull` shows them as
  empty strings. They are still available to builds and functions — this is expected Vercel
  behavior, not a sync failure.
</Callout>

After changing envs, re-run the sync **and redeploy** — environment changes only take effect on the next deployment.

## Local Development [#local-development]

`bun dev` remains your primary loop — native hot reload, no Vercel involvement. Two Vercel-specific commands complement it:

* `bun dev:vercel` runs `vercel dev` for validating rewrites and env parity against the platform's routing layer. It is slower than `bun dev`; use it as a checking tool, not a daily driver.
* `bun deploy:check` runs `vercel deploy --dry` — it prints the detected framework presets and the exact files a deployment would include, without uploading anything. Useful before a first deploy or after changing `vercel.json`.

## Command Reference [#command-reference]

| Command              | What it does                                          |
| -------------------- | ----------------------------------------------------- |
| `bun dev:vercel`     | Run the Vercel Services dev environment locally       |
| `bun env:preview`    | Sync local `.env` files to the preview environment    |
| `bun env:production` | Sync local `.env` files to the production environment |
| `bun deploy:check`   | Dry-run a deploy (no upload)                          |
| `bun deploy`         | Create a preview deployment                           |
| `bun deploy:prod`    | Deploy to production                                  |

<Callout type="info">
  If a project deploys web and server to different platforms (Vercel + Cloudflare), the deploy
  scripts are named by target instead: `deploy:web` / `deploy:web:prod` for one side and
  `deploy:server` for the other. Setup, env sync, and dry-run scripts keep the same names.
</Callout>

## Troubleshooting [#troubleshooting]

### "Environment variable X is undefined" after deploying [#environment-variable-x-is-undefined-after-deploying]

You deployed before syncing envs. Run `bun env:production` (or `:preview`), then redeploy.

### `vercel link --yes` fails with "Provide --team or --scope" [#vercel-link---yes-fails-with-provide---team-or---scope]

Your account belongs to multiple teams. Pass the scope explicitly: `bunx vercel link --yes --scope your-team`.

### `vercel env pull` returns empty values [#vercel-env-pull-returns-empty-values]

Synced variables are stored as Sensitive and cannot be read back. They still work in builds and at runtime. Check `vercel env ls production` to confirm they exist.

### API calls fail with CORS errors (server-only deployment) [#api-calls-fail-with-cors-errors-server-only-deployment]

With a server-only deployment your web app lives on a different domain, so the runtime-derived `CORS_ORIGIN` (the server's own origin) is wrong. Set `CORS_ORIGIN` to your web app's URL in `apps/server/.env` and re-sync.

### SSR RPC calls return 401 on preview deployments [#ssr-rpc-calls-return-401-on-preview-deployments]

Browser-side RPC always works — it calls the same origin the page was served from. But **server-side** RPC (SSR frameworks like Next.js or TanStack Start) reconstructs an absolute URL from `VERCEL_URL`, which routes through the public deployment URL. With [Deployment Protection](https://vercel.com/docs/deployment-protection) enabled (Standard Protection is Vercel's default), preview deployment URLs require authentication, so those server-to-server fetches get a 401. Production is unaffected — the production domain is not protected.

Workarounds: set a [Protection Bypass for Automation](https://vercel.com/docs/deployment-protection/methods-to-bypass-deployment-protection/protection-bypass-automation) secret and send it from server-side fetches, relax protection for previews in the project settings, or avoid server-side RPC on preview deployments. [Service bindings](https://vercel.com/docs/services/bindings) (private service-to-service URLs) are the long-term fix and are on the roadmap for this template.

### The browser calls `/api` but gets the web app's 404 [#the-browser-calls-api-but-gets-the-web-apps-404]

Check that `vercel.json` still contains both rewrites in order — `/api/(.*)` to the server service must come before the catch-all. The dry-run (`bun deploy:check`) shows what Vercel detected.

### Changed an env var but the app still sees the old value [#changed-an-env-var-but-the-app-still-sees-the-old-value]

Envs are read at build/boot time. Re-run the env sync, then redeploy.
