Skip to content

Repository files navigation

tstack

tstack

The boring parts of a SaaS, already built.

A free, open-source SvelteKit SaaS boilerplate / starter template: authentication, organizations, multi-tenancy, transactional email, image uploads, a themed component library and a worked CRUD example — so your first commit is your product, not your plumbing.

CI License: MIT Svelte 5 SvelteKit Prisma 7 Tailwind CSS 4 daisyUI 5 bun PRs welcome

Quickstart · Features · Deploy · Architecture · Building with AI agents · Contributing

The dashboard of a freshly cloned tstack, still under its placeholder name

The screenshots show the template exactly as it clones — “MyApp” is the placeholder in src/lib/config.json, one line away from your own name.

Sign in Tasks (the CRUD example)
Sign-in page Task list with search, filters and sorting
Members page with roles and invitations Dashboard in dark mode

Component gallery — every element ships with a live reference page at /components:

Component gallery in dark mode

What's inside

  • Auth that works without JavaScriptBetter Auth with email + password, email verification, password reset, and optional Google/GitHub OAuth. Every flow is a progressively-enhanced form.
  • Organizations & multi-tenancy — workspaces with roles, invitations and an org switcher. Every query is scoped to the active organization, including bulk writes.
  • Remote functions only — the single client↔server data path is SvelteKit remote functions; every one starts with an authorization guard.
  • A worked CRUD example/crud is a task list with search, filters, sorting and "load more", where the entire list state lives in the URL. Copy the pattern for your own models.
  • Component libraryButton, Field, Modal, DataTable, Toaster and friends on Tailwind 4 + daisyUI 5, with light/dark themes and a live gallery at /components.
  • Transactional emailResend with one consistent message shape; without an API key every mail is printed to the terminal, so local sign-up just works.
  • Image uploads — every upload goes through sharp into preset-sized WebP, served with a session gate and path-traversal checks.
  • Typed everything — Prisma 7 (Postgres), Zod, explicit environment variable declarations, strict TypeScript, ESLint + Prettier.
  • Tested — unit, browser component and HTTP integration tests, all running in CI.

Quickstart

You need bun and a Postgres. No Postgres at hand? bunx prisma dev starts a local one and prints the connection URL.

bun install
cp .env.example .env   # set DATABASE_URL, BETTER_AUTH_SECRET, BETTER_AUTH_URL
bun run db:migrate     # apply migrations
bun run db:seed        # demo users, two organizations, a task list
bun run dev

BETTER_AUTH_SECRET signs the session cookie — generate one with openssl rand -base64 32. BETTER_AUTH_URL has to be the origin the app is reachable at, port included; the links in verification and invitation emails are built from it, and the app refuses to start without it outside development.

Leave RESEND_API_KEY empty to start: every email is then printed to the terminal instead of sent, which is what makes signing up locally possible at all given that the address has to be confirmed first. The seeded accounts skip that — sign in as owner@example.com with the password demo-password.

Make it yours

  1. Name itapp.name in src/lib/config.json, plus name in package.json. The logo, every page title, the footer and the workspace URL prefix read from there.
  2. Brand itapp.icon in the same file picks the logo mark (any name from src/lib/components/elements/icons.ts); the two themes live in src/lib/css/main.css.
  3. Retell it — the landing copy, the panel beside the sign-in form and the navigation sit in src/lib/config.json as well, so the public pages can be rewritten without touching a component.
  4. Replace the demo content — the dashboard figures and billing plans still come from src/lib/helper/demo.ts; delete an export there and follow the type errors.
  5. Add your first model — copy the /crud pattern: a Prisma model, a list config next to src/lib/helper/task.ts, a remote function, a page. The architecture guide walks through it.

Scripts

Script Purpose
bun run dev / build / preview Vite dev server / production build / preview
bun run start Serve the production build (node build/index.js)
bun run check svelte-check type checking
bun run lint / format Prettier + ESLint / auto-format
bun run test Vitest (unit + browser component tests)
bun run test:integration HTTP integration tests against the production build (needs a DB)
bun run db:migrate Create/apply Prisma migrations (development)
bun run db:deploy Apply committed migrations (production/CI)
bun run db:generate Regenerate the Prisma client into src/generated
bun run db:seed Seed demo users, organizations and tasks (skips when there are users)
bun run db:studio Prisma Studio

Testing

Three layers, all running in CI:

  • Unit tests — colocated *.spec.ts next to the helpers they test.
  • Component tests*.svelte.spec.ts in real Chromium via Vitest browser mode.
  • Integration teststests/integration/ boots the production build against a real Postgres and exercises sign-in, the auth wall and the seeded workspace over plain HTTP: bun run build && bun run test:integration.

Deploy to Railway

Deploy on Railway

The repo ships a railway.json that builds with bun, runs prisma migrate deploy before each deployment and serves build/index.js. After clicking the button (or creating a service from your fork):

  1. Add a Postgres to the project and reference its URL: DATABASE_URL = ${{Postgres.DATABASE_URL}}.
  2. Set the secretsBETTER_AUTH_SECRET (openssl rand -base64 32), and point both BETTER_AUTH_URL and ORIGIN at your public origin, e.g. https://${{RAILWAY_PUBLIC_DOMAIN}}.
  3. Attach a volume and set UPLOAD_DIR to its mount path, e.g. /data/uploads — anything inside the app directory is wiped on the next deployment, along with every avatar and logo.

Details, other platforms and the sharp caveat: docs/DEPLOYMENT.md.

Building with AI agents

This template is written to be worked on by AI coding agents — the conventions are few, explicit and machine-checkable:

  • AGENTS.md — the contract: architecture rules, guardrails and the commands an agent should run before calling anything done. CLAUDE.md points Claude Code at the same file.
  • Svelte MCP server.mcp.json connects agents to the official Svelte MCP server for current Svelte 5 / SvelteKit docs and an autofixer, so agents don't write Svelte 4 from memory.
  • Guarded by design — authorization lives in src/lib/server/guard.ts and every remote function starts with it; the layering means a generated feature is safe by construction, not by review.
  • Types as the safety net — deleting a demo export or renaming a field produces type errors that lead an agent through every affected file.

Documentation

  • Architecture — remote functions, auth, multi-tenancy, uploads, email, theming, lists.
  • Deployment — Railway step by step, environment variables, migrations.
  • Contributing — dev setup, checks, how to get a PR merged.
  • AGENTS.md — the guide AI coding agents (and new humans) should read first.

Contributing

Found a bug, a rough edge in the docs, or a pattern that deserves to be in the template? PRs and issues are very welcome — this project grows by being used. Good first contributions: another OAuth provider, a Stripe-backed billing page to replace the demo plans, more list configs, an S3-compatible upload target, translations of the content pages. See CONTRIBUTING.md to get started — the whole test suite runs with two commands, so you'll know quickly whether your change holds.

If tstack saved you a week of plumbing, a ⭐ on the repo helps others find it.

License

MIT — use it, fork it, ship your SaaS with it.

About

SvelteKit SaaS boilerplate — Better Auth with organizations & multi-tenancy, Prisma 7, Tailwind 4 + daisyUI, email, uploads, tests, one-click Railway deploy.

Topics

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages