Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
153 changes: 30 additions & 123 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,150 +1,57 @@
# 🏝️ tano-stack
# tano-stack

My personal full-stack starter I put together after trying out a bunch of different stacks. It's got everything I need for most projects without being overly complicated.
My personal starter for building web apps with the stack I actually use.
Not trying to be everything for everyone, just a solid setup that saves me from redoing the same plumbing every weekend.

## What's Inside
## Tech Stack

- **⚑ TanStack Start** β€” File-based routing, server functions, and SSR that just work
- **πŸ” Better Auth** β€” Rock-solid authentication with email verification and social logins
- **πŸ—ƒοΈ Drizzle + PostgreSQL + TanStack DB** β€” Type-safe queries, effortless migrations, connection pooling
- **πŸ“§ React Email + useSend** β€” Transactional emails that look professional and send reliably
- **🎨 shadcn/ui** β€” Beautiful, accessible components that feel custom-built
- **βš™οΈ pg-boss** β€” Background jobs using your existing Postgres database
- **πŸ›‘οΈ Cloudflare Turnstile** β€” Bot protection that doesn't annoy real users
- **πŸŒ“ Dark/Light Mode** β€” Seamless theme switching built-in
- **πŸ”§ Bun + Vite** β€” Development that's actually fast and enjoyable
- TanStack Start (React, SSR, file-based routing)
- Better Auth (email + social login)
- Drizzle ORM + PostgreSQL
- TanStack Query + TanStack DB
- Tailwind CSS v4 + shadcn/ui
- pg-boss (background jobs)
- React Email + useSend
- Bun + Vite

## CI/CD

[![CI](https://github.com/kegren/tano-stack/actions/workflows/ci.yml/badge.svg)](https://github.com/kegren/tano-stack/actions/workflows/ci.yml)

Automated quality checks run on every commit:
- **Linting** β€” Code style and logic checks with Ultracite
- **Testing** β€” Unit tests with Vitest
- **Build** β€” Production build verification
GitHub Actions runs on pushes to `main` and on PRs:

## Modern Features
- lint (`bun lint`)
- tests (`bun test`)
- build (`bun run build`)

- **🎨 Dark/Light Mode** β€” Theme switching with `better-themes` and CSS variables
- **⚑ Optimistic Updates** β€” Instant UI feedback with TanStack DB's mutation features
- **🏊 Connection Pooling** β€” Efficient PostgreSQL connection management with `postgres.js`
- **πŸ“Š Type-Safe Everything** β€” TanStack Query provides additional type safety on top of Drizzle
- **πŸ”„ Background Processing** β€” Email sending and async tasks with `pg-boss`
- **πŸ›‘οΈ Security First** β€” Rate limiting, CAPTCHA, CSRF protection, and secure sessions
- **πŸ“± Mobile-First UI** β€” Responsive design with Tailwind CSS and shadcn/ui
- **πŸ”‘ Social Auth** β€” Email + Google/GitHub sign in with Better Auth
- **πŸš€ Production Ready** β€” Built-in error boundaries, loading states, and performance optimizations
If something breaks, CI yells first so production does not have to.

## Features

- Auth flows ready to go (email verification + social)
- Type-safe DB setup with Drizzle migrations
- Web + worker split for background processing
- Turnstile support for bot protection
- Dark/light theme toggle
- Production build + test setup out of the box

## Quick Start

```bash
# Get the code
git clone https://github.com/kegren/tano-stack
cd tano-stack

# Install everything
bun install

# Set up your environment
cp .env.example .env
# Add your database URL, auth secrets, and API keys

# Push schema to database
bun db:push

# Start building
bun dev
```

Visit [http://localhost:3000](http://localhost:3000) and you'll have a full-stack app running with auth, database, and email ready to go.
Then open `http://localhost:3000`.

## Deploy to Railway

This template is designed for Railway with two services: a web app and a background worker.

### Prerequisites
- [Railway account](https://railway.app)
- PostgreSQL database (Railway provides this)

### 1. Database Setup
Create a PostgreSQL database in Railway and copy the `DATABASE_URL`.

### 2. Deploy Web Service
```bash
# Fork this repo and connect to Railway
# Set these environment variables:
DATABASE_URL=postgresql://...
BETTER_AUTH_SECRET=your-secret-here
GITHUB_CLIENT_ID=your-github-client-id
GITHUB_CLIENT_SECRET=your-github-client-secret
GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secret
TURNSTILE_SITE_KEY=your-turnstile-site-key
TURNSTILE_SECRET_KEY=your-turnstile-secret-key
USE_SEND_API_KEY=your-usesend-api-key

# Railway will auto-detect and deploy using:
# Build command: bun run build
# Start command: bun run start
```

### 3. Deploy Background Worker
Create a second service in Railway with the same repo but different start command:

```bash
# In Railway service settings:
# Build command: bun run build
# Start command: bun run worker:start
```

### Architecture
- **Web Service**: Handles HTTP requests, auth, and UI
- **Worker Service**: Processes background jobs (email sending, etc.)
- **Database**: Shared PostgreSQL instance

### Environment Variables
Both services need the same `DATABASE_URL`. The web service needs all auth/API keys, while the worker only needs database access.

Railway's free tier works great for getting started!

## Project Structure

```
src/
β”œβ”€β”€ features/ # Feature modules (auth, todos, etc.)
β”œβ”€β”€ components/ # Shared UI components
β”œβ”€β”€ routes/ # File-based routing with TanStack Router
β”œβ”€β”€ lib/ # Utilities, auth config, email setup
β”œβ”€β”€ db/ # Database schema and migrations
└── worker/ # Background job processing
```
## PRs (Welcomed)

## Commands

| Command | Description |
|---------|-------------|
| `bun dev` | Start dev server + background worker |
| `bun dev:web` | Dev server only (port 3000) |
| `bun dev:worker` | Background worker only |
| `bun build` | Production build |
| `bun serve` | Preview production build |
| `bun start` | Start production server |
| `bun worker:start` | Start background worker |
| `bun test` | Run test suite |
| `bun typecheck` | TypeScript type checking |
| `bun lint` | Code linting |
| `bun lint:fix` | Auto-fix linting issues |
| `bun db:generate` | Generate Drizzle migrations |
| `bun db:migrate` | Run pending migrations |
| `bun db:push` | Push schema changes directly |
| `bun db:pull` | Pull schema from database |
| `bun db:studio` | Open Drizzle Studio GUI |
| `better-auth:generate` | Update auth schema |
| `better-auth:secret` | Generate new auth secret |

## Contributing

Love that you're interested! This started as my personal toolkit, but I'm excited to see how others use and improve it. PRs welcome.
If you spot something weird, clunky, or just mildly annoying, PRs are very welcome.
Small fixes, big improvements, and opinionated tweaks all appreciated.

## License

Expand Down
32 changes: 16 additions & 16 deletions src/routes/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ function App() {
{/* Hero Content */}
<div className="space-y-4 sm:space-y-6">
<h1 className="font-semibold text-3xl text-foreground sm:text-5xl lg:text-6xl">
Build modern web apps
<br className="hidden sm:block" /> faster with tano-stack
tano-stack
<br className="hidden sm:block" /> my go-to web app starter
</h1>

<p className="mx-auto max-w-xl text-muted-foreground text-sm sm:text-base lg:text-lg">
A full-stack starter I built after testing different approaches. Practical, type-safe, and not over-engineered.
Built so I can stop wiring auth, DB, and jobs from scratch every time I start a new idea.
</p>
</div>

Expand Down Expand Up @@ -73,42 +73,42 @@ function App() {
<div className="flex flex-col items-center space-y-2 rounded-lg border p-4 text-center">
<div className="text-2xl">πŸŒ“</div>
<h3 className="font-medium">Dark/Light Mode</h3>
<p className="text-muted-foreground text-sm">Seamless theme switching with better-themes</p>
<p className="text-muted-foreground text-sm">Theme toggle included, no extra setup rabbit hole</p>
</div>
<div className="flex flex-col items-center space-y-2 rounded-lg border p-4 text-center">
<div className="text-2xl">⚑</div>
<h3 className="font-medium">Optimistic Updates</h3>
<p className="text-muted-foreground text-sm">Instant UI feedback with TanStack DB</p>
<h3 className="font-medium">Fast UI by Default</h3>
<p className="text-muted-foreground text-sm">Optimistic updates so the app feels snappy</p>
</div>
<div className="flex flex-col items-center space-y-2 rounded-lg border p-4 text-center">
<div className="text-2xl">πŸ”„</div>
<h3 className="font-medium">Background Jobs</h3>
<p className="text-muted-foreground text-sm">Email sending & async tasks with pg-boss</p>
<p className="text-muted-foreground text-sm">Emails and async work handled by a worker process</p>
</div>
<div className="flex flex-col items-center space-y-2 rounded-lg border p-4 text-center">
<div className="text-2xl">🏊</div>
<h3 className="font-medium">Connection Pooling</h3>
<p className="text-muted-foreground text-sm">Efficient PostgreSQL connection management</p>
<h3 className="font-medium">Database Ready</h3>
<p className="text-muted-foreground text-sm">Postgres + pooling already wired up</p>
</div>
<div className="flex flex-col items-center space-y-2 rounded-lg border p-4 text-center">
<div className="text-2xl">πŸ“Š</div>
<h3 className="font-medium">Type-Safe Database</h3>
<p className="text-muted-foreground text-sm">Drizzle ORM + TanStack Query for full type safety</p>
<p className="text-muted-foreground text-sm">Drizzle and TanStack tools keep types honest</p>
</div>
<div className="flex flex-col items-center space-y-2 rounded-lg border p-4 text-center">
<div className="text-2xl">πŸ”</div>
<h3 className="font-medium">Secure by Default</h3>
<p className="text-muted-foreground text-sm">Rate limiting, CAPTCHA, and session security</p>
<h3 className="font-medium">Auth That Ships</h3>
<p className="text-muted-foreground text-sm">Email + social auth with the safety bits in place</p>
</div>
<div className="flex flex-col items-center space-y-2 rounded-lg border p-4 text-center">
<div className="text-2xl">βœ…</div>
<h3 className="font-medium">CI/CD Ready</h3>
<p className="text-muted-foreground text-sm">Automated testing, linting, and build checks</p>
<h3 className="font-medium">CI Included</h3>
<p className="text-muted-foreground text-sm">Lint, test, and build checks on pushes and PRs</p>
</div>
<div className="flex flex-col items-center space-y-2 rounded-lg border p-4 text-center">
<div className="text-2xl">πŸ”‘</div>
<h3 className="font-medium">Social Auth</h3>
<p className="text-muted-foreground text-sm">Email + Google/GitHub sign in ready</p>
<p className="text-muted-foreground text-sm">Google/GitHub sign-in ready when you need it</p>
</div>
</div>
</div>
Expand Down Expand Up @@ -136,7 +136,7 @@ function App() {
rel="noopener noreferrer"
target="_blank"
>
View on GitHub
Curious? Peek at the repo
</a>
</p>
</div>
Expand Down