Introduction
What ShipKit is, what the template ships with, the stack it is built on and why, and how the repository is laid out.
ShipKit is a SaaS starter: a working application with sign-in, payments, credits, an admin console, email, i18n and a marketing site, running on TanStack Start and Cloudflare Workers. You clone it, rename it, delete what you do not need and build your product in the space that is left.
It is written around one rule: every optional feature must be deletable in
minutes. Each module lives in its own folder, touches shared files only in
marked places, and ships with a deletion recipe that CI runs on every pull
request to prove the app still builds without it. The second design goal is
that a coding agent can work in the repo without breaking it: the conventions
are written down in CLAUDE.md and AGENTS.md, and the important ones are
enforced by tests rather than trusted.
What you get
| Area | What ships |
|---|---|
| Sign-in | Passwordless, through better-auth: Google (with One Tap), GitHub when configured, and a six-digit code emailed to any address. No passwords, so no reset flow |
| Payments | Stripe and Creem behind one provider-neutral event layer: subscriptions, one-time credit packs, checkout, customer portal, refunds. Stripe is the default |
| Credits | A ledger with monthly grants per plan, purchasable packs, overdraft-proof spending and admin adjustments |
| Admin console | /admin, in five sections: overview (KPIs and charts), people (users, waitlist, feedback), revenue (subscriptions, orders, credits, affiliates), developers (API keys, audit log, email log) and settings (operator settings, roles, email previews) |
| Permissions | Role-based access with wildcard grants. Each feature declares its permissions; the same grant gates the page, the server function and the API |
| API | Read-only JSON APIs with two kinds of API key: /api/v1/admin/* for your own analysis, /api/v1/me/* for a customer's own data |
| Email and notifications | Transactional email through Resend, localized per recipient, with a delivery log; an in-app notification bell |
| Files | Uploads to R2, streamed through the Worker; profile photos |
| i18n | English and Chinese through Paraglide, English unprefixed and /zh/... for Chinese |
| Content | MDX blog, docs and legal pages (terms, privacy, refunds, DMCA), compiled at build time |
| Marketing site | A landing page assembled from a config array of blocks, a pricing page, four visual themes with light and dark modes |
| More modules | Audit log, feedback, waitlist, affiliate program, PostHog analytics, and a public demo mode |
| Account self-service | Profile, language, data export as JSON, and account deletion with a retention window |
Each of these has its own page under Features in the sidebar.
The stack, and why
| Layer | Choice | Why |
|---|---|---|
| Framework | TanStack Start + TanStack Router and Query | Type-safe file routes, server functions and loaders in one React app, built on Vite |
| Runtime | Cloudflare Workers | One deploy target at the edge; the dev server runs your code in workerd, the same runtime as production |
| Database | Cloudflare D1 (SQLite) + Drizzle | A binding, not a connection string: no pool, no credentials, local and remote behave the same |
| Storage | Cloudflare R2 | Also a binding, so uploads need no S3 keys or presigned URLs |
| Auth | better-auth | Sessions and OAuth in your own database, with plugins for One Tap, emailed codes, admin and API keys |
| Payments | Stripe or Creem | Both wired; PAYMENT_PROVIDER picks which takes new checkouts. See Payments |
| i18n | Paraglide | Messages compile to tree-shakable functions; no runtime catalog |
| Content | MDX via @mdx-js/rollup | Compiled to ES modules at build time, because workerd forbids the runtime evaluation most MDX layers rely on |
| UI | shadcn/ui + Tailwind CSS v4 | Components you own and edit, styled with tokens |
| Tooling | bun, Biome, Vitest, Playwright | One package manager, one linter and formatter, unit and end-to-end tests |
A few limitations come with these choices, and it is better to know them up front:
- TanStack Start is still a v1 release candidate. Versions are pinned exactly; read the changelog before you bump them.
- D1 has no interactive transactions. Multi-step writes use a single
conditional statement or
db.batch(), as described in Database. - The template targets Cloudflare only. Moving to another host means replacing the bindings layer, not changing a setting.
- In-app plan changes, failed-renewal handling and provider-side customer redaction are Stripe only.
How the repository is laid out
src/
├── routes/ # thin route files: guards and composition
│ ├── _marketing/ # public pages, prerendered at build
│ ├── _auth/ # login
│ ├── _app/ # signed-in shell: dashboard, billing, credits,
│ │ # files, settings and the /admin console
│ └── api/ # auth, webhooks, file streams, the /api/v1 APIs
├── features/ # vertical slices: auth, billing, credits, files, ...
├── core/ # shared kernel: db, server, payment, email, authz, ...
├── config/ # app-config, plans, landing page, themes
└── components/ # shared UI, the sidebar, marketing blocks
content/ # blog, docs and legal pages as MDX
messages/ # UI copy, en.json and zh.json
drizzle/ # SQL migrations
scripts/ # seed, deletion recipes, sitemap, marketing captures
A feature folder such as src/features/credits/ holds its own schema, server
functions, components and queries. It reaches the rest of the app through
small registries in src/core/, one import line per feature, so removing a
feature means deleting its folder and a handful of marked lines.
Architecture explains how the pieces connect.
Every read and write from the UI follows the same path: route loader, then
TanStack Query, then a server function, then Drizzle. API routes are kept for
external callers: auth, webhooks, file streams and the /api/v1 APIs.
Server functions covers the details.
Where to go next
- Getting started: run the app locally and make yourself an admin.
- Working with AI agents: the skills and guard-rail tests, if you build with Claude Code, Codex or Cursor.
- Configuration: the config files, environment variables and admin-editable settings.
- Deploy: put it on Cloudflare.
- Deleting features: strip the modules you do not need before you start building.