Tech Stack
Every library in the box, why it was picked, and where to find its config.
Launchy is opinionated. Every dependency is chosen to minimize boilerplate and maximize type safety. This page is the map.
Runtime & framework
| Tool | Version | Why |
|---|---|---|
| Next.js | 16 (App Router) | Server components, route handlers, streaming, middleware, edge/node split. |
| React | 19 | Server components default, actions, useFormStatus, async components. |
| TypeScript | 5.x strict | strict: true + noUncheckedIndexedAccess — the safety net. |
| Bun | 1.x | Fast installer + script runner. The project also works with pnpm / npm if you prefer. |
Config lives in tsconfig.json, next.config.mjs, package.json.
Styling
| Tool | Role |
|---|---|
| Tailwind CSS 4 | Utility-first styling with CSS-first config (no tailwind.config.js). |
| shadcn/ui | Copy-paste component primitives in components/ui/ — you own them, customize freely. |
| tw-animate-css | Micro-animation utilities for entrance / hover. |
| Lucide icons | Icon set used throughout. |
Theme tokens are defined in app/globals.css under @theme inline. Changing --color-foreground, --color-background, etc. instantly re-themes the whole site.
Data layer
| Tool | Role |
|---|---|
| PostgreSQL 15+ | Relational database. |
| Drizzle ORM | Type-safe SQL. Schema in lib/db/schema.ts. |
| drizzle-kit | Migrations via bun db:push (dev) or bun db:migrate (prod). |
All server code imports from @/lib/db. The schema is also the source of truth for validation types — there's no separate types/ mirror.
Auth
Better Auth — open-source, typed, framework-agnostic. Handles:
- Email/password with verification
- Google OAuth
- GitHub OAuth
- Sessions (Postgres-backed, httpOnly cookies)
- Admin roles via the
rolecolumn
Config in lib/auth.ts.
Payments
Stripe — Checkout Sessions (no Elements), webhooks for fulfillment, refund flow.
- Paid product tiers (Boost / Highlight) when a user upgrades a submission.
- Self-serve sponsor slot booking with volume discounts.
- Refund → webhook revokes access automatically.
Keys in env.ts under STRIPE_*.
Storage
Cloudflare R2 (S3-compatible) with local fallback.
- In production: all uploads go to R2, served via
R2_PUBLIC_URL. - In dev without R2 configured: uploads land in
public/uploads/on your disk.
Helpers in lib/r2.ts.
Emails
React Email templates + Plunk delivery.
- Templates are React components in
emails/. Each compiles to HTML via@react-email/components. - Plunk sends transactional + campaign emails. Free tier covers 3k/month.
Helpers in lib/emails.ts and lib/plunk.ts.
Rate limiting & background work
Redis (via ioredis) for:
- Rate limiting signup, voting, submission, comment, checkout
- Weekly-batch idempotency lock
- Sliding-window counters for abuse detection
Helpers in lib/redis.ts and lib/rate-limit.ts.
Content & editor
| Tool | Role |
|---|---|
| Tiptap | WYSIWYG rich-text editor for blog + product descriptions. |
| Shiki | Syntax highlighting inside rendered content. |
| rehype / remark | MDX pipeline for transforming code blocks, anchors, TOC. |
Observability
| Tool | Role |
|---|---|
| Plausible | Privacy-first web analytics (self-hostable). Proxied via next-plausible. |
| Discord webhook | Admin alerts (new submission, new sponsor booking, cron failures). |
Dev tooling
- ESLint flat config in
eslint.config.mjs - Prettier with
prettier-plugin-tailwindcssfor Tailwind class sorting - nuqs for URL-driven query state (admin tables, filters)
What's not included (on purpose)
- No tRPC / GraphQL — server actions + route handlers are enough for this app's shape
- No Redux / Zustand — all data lives either in the DB (via RSC) or URL (nuqs)
- No test runner — you're free to add Vitest or Playwright if you want; not bundled to keep the template lean
If you need any of these, they integrate cleanly. Nothing in Launchy locks you out of them.