Free while in beta
Skip the boilerplate. Keep your stack. Ship the product.
Answer a few questions — framework, components, database, ORM, auth, billing, email, landing page, package manager — and the whole repo downloads, wired together, with its test suite already written and green on the first run.
- ▸ public/
- ▸ src/
- ▸ app/
- ▸ (app)/
- ▸ dashboard/
- ▸ dashboard/
- ▸ (auth)/
- ▸ sign-in/
- ▸ sign-up/
- ▸ sign-in/
- ▸ (marketing)/
- ▸ api/
- ▸ auth/
- ▸ [...all]/
- ▸ [...all]/
- ▸ health/
- ▸ auth/
- ▸ (app)/
- ▸ components/
- ▸ auth/
- ▸ ui/
- ▸ auth/
- ▸ db/
- ▸ lib/
- ▸ server/
- ▸ app/
import {
boolean,
pgTable,
text,
timestamp,
} from "drizzle-orm/pg-core";
/**
* Better Auth's tables. The names and columns are the ones it expects — it
* reads and writes these directly, so renaming a column here breaks sign-in.
*
* Your own tables belong beside them, referencing `user.id`.
*/
export const user = pgTable("user", {
id: text("id").primaryKey(),
name: text("name").notNull(),
email: text("email").notNull().unique(),
emailVerified: boolean("email_verified").notNull().default(false),
image: text("image"),
createdAt: timestamp("created_at").notNull().defaultNow(),
updatedAt: timestamp("updated_at").notNull().defaultNow(),
});
export const session = pgTable("session", {
id: text("id").primaryKey(),
expiresAt: timestamp("expires_at").notNull(),
token: text("token").notNull().unique(),
createdAt: timestamp("created_at").notNull().defaultNow(),
updatedAt: timestamp("updated_at").notNull().defaultNow(),
ipAddress: text("ip_address"),
userAgent: text("user_agent"),
userId: text("user_id")
.notNull()
.references(() => user.id, { onDelete: "cascade" }),
});
export const account = pgTable("account", {
id: text("id").primaryKey(),
/* Required since better-auth 1.7: it refuses to start without it. */
issuer: text("issuer").notNull(),
accountId: text("account_id").notNull(),
providerId: text("provider_id").notNull(),
userId: text("user_id")
.notNull()
.references(() => user.id, { onDelete: "cascade" }),
accessToken: text("access_token"),
refreshToken: text("refresh_token"),
idToken: text("id_token"),
accessTokenExpiresAt: timestamp("access_token_expires_at"),
refreshTokenExpiresAt: timestamp("refresh_token_expires_at"),
scope: text("scope"),
password: text("password"),
createdAt: timestamp("created_at").notNull().defaultNow(),
updatedAt: timestamp("updated_at").notNull().defaultNow(),
});
export const verification = pgTable("verification", {
id: text("id").primaryKey(),
identifier: text("identifier").notNull(),
value: text("value").notNull(),
expiresAt: timestamp("expires_at").notNull(),
createdAt: timestamp("created_at").notNull().defaultNow(),
updatedAt: timestamp("updated_at").notNull().defaultNow(),
});
export type User = typeof user.$inferSelect;
export type NewUser = typeof user.$inferInsert;
Pick the tools you already trust
- TanStack Start
- Next.js
- React 19
- Neon
- Drizzle ORM
- Prisma
- Better Auth
- Tailwind CSS
- Biome
- Vercel
Any stack. Any provider. Already wired.
Every tile below is a module the generator can wire for you. No single starter contains all of them — the grid is the menu, and the questions are what pick from it.
- Next.jsApp Router + layoutsWiring
- Next.jsServer actionsWiring
- TanStack StartFile-based routingWiring
- TanStack StartServer-side renderingWiring
- TanStack RouterTyped route treeWiring
- TanStack QueryServer state cachingWiring
- Better AuthEmail + password sign-inWiring
- Better AuthCookie sessionsWiring
- Drizzle ORMTyped table schemaWiring
- Drizzle KitMigrations + studioWiring
- PrismaGenerated clientWiring
- Prisma MigrateVersioned migrationsWiring
- NeonServerless Postgres driverWiring
- NeonBranch per previewWiring
- Tailwind CSSv4 design tokensWiring
- Tailwind CSSDark mode variantWiring
- TypeScriptStrict compilerWiring
- TypeScriptPath aliasesWiring
Tested by default
Every module arrives with its own Vitest suite — unit and component tests, strict types, and lint behind a single command. Every combination we generate is green in CI before anyone can pick it.
Tests ship with the feature
Every module we generate lands with a spec file beside it, so your first commit starts covered instead of catching up later.
Strict by default
Strict TypeScript and Biome arrive preconfigured — one command lints, formats, and type-checks whatever you picked.
Coverage from day one
v8 coverage comes wired in. Run one script and see exactly what the suite proves about your repo.
Modularity
Swap anything
Every question the wizard asks maps to an adapter, not a fork. Picking Prisma over Drizzle changes the files below and nothing above them.
This is also why the list can grow. A new framework or a new database is another adapter behind the same seam, so adding one does not multiply the templates we maintain — and every combination still has to go green in CI before it is offered.
- app entrygenerated
- routinggenerated
- server handlersgenerated
The widest choice on the list, and the reason the rest are cheap: routing and rendering live in a framework adapter, so nothing above them has to know which one you picked. React + Vite is the one that changes what else is possible — a browser-only app has nowhere to keep a secret, so the questions after it narrow to what can run without a server.
3 places change · routes, components, and business logic untouched
Where is authentication configured?
- src/
- db/
- client.ts
- schema.ts
- schema.test.ts
- lib/
- auth.ts
- auth.test.ts
- checkout.ts
- checkout.test.ts
- styles.css
src/lib/auth.ts
One module per integration, named after the thing it integrates. Nothing else reaches for the provider.
Built for assistants
AI reads it correctly
Every repo we generate has the same shape: predictable layout, typed boundaries, and a spec beside every module. An assistant can guess where something lives — and when it guesses wrong, the compiler says so before you run anything.
- One module per concern
- Every integration gets a file named after it, in the same place in every repo we generate. There is no registry to keep in sync and no indirection to trace before making a change.
- Wrong guesses fail loudly
- strictnoUnusedLocalsnoUnusedParametersnoFallthroughCasesInSwitchnoUncheckedSideEffectImportsverbatimModuleSyntax
- Intent is executable
- Each module's test file states what it must do. That is context an assistant can run, not prose it has to trust.
How it works
Ten answers and the repo is on your machine
Every question narrows the next one, so you cannot assemble a stack that does not fit together. You see the whole repo before it is created.
Answer ten questions
Framework, components, database, ORM, auth, billing, and a name. Each answer narrows the next, so a combination that does not work is never offered in the first place.
See it before it exists
The full file tree and the suite it ships with, laid out before anything is generated. Nothing is built until you approve it.
Take delivery
A zip of the whole repo, yours to unpack and push wherever you like. Install, run the suite, and it is green on the first run.
Where it deploys is the one question we do not ask. Guessing your host would only give you a config file to delete, so the repo arrives without one.
Use cases
Your starting point, whatever you are building
We do not sell a vertical template for each of these. They need the same plumbing wired to different choices — which is the one thing a generator is better at than a repo you clone.
Sign-in and sessions arrive configured against whichever auth provider you picked, so the account model is the first thing you extend rather than the first thing you build.
Sign in
Password
Cookie session, no extra wiring
Start with the tests already written
Answer ten questions. The whole repo downloads, ready to push wherever you like, with the suite already green. Free while in beta.