EngineeringApril 30, 20261 min
The stack and tools I actually use
What I bring into projects in 2026, what I've thrown out, and why the same stack works for an online shop and a SaaS alike.
People often ask: "what's your stack right now?" Here's the long answer — because tooling choices directly shape cost of ownership and shipping speed.
Frontend
The baseline I bring to most projects:
- Next.js 16 (App Router, RSC by default)
- React 19 —
use, server actions, optimistic updates - Tailwind CSS v4 — no config, via
@theme inline - shadcn/ui + Radix Primitives — copy into the repo, don't pull as a dep
I used to spend a week on a "design system from scratch." Now shadcn gets me 80% of the UI in an evening, and the remaining 20% — that's the product.
Backend
| Concern | Tool | Why |
|---|---|---|
| API | Next.js Route Handlers / tRPC | Single repo, types between client and server |
| Database | PostgreSQL (Neon) | Free tier, branching, no vendor lock-in |
| ORM | Drizzle | Type safety without runtime overhead |
| Auth | Clerk / Auth.js | Depends on budget and SSO requirements |
| Queues | Vercel Queues | Out of beta, replaced BullMQ for me |
Drizzle example
Here's a minimal schema for a simple shop:
import { pgTable, serial, text, integer, timestamp } from "drizzle-orm/pg-core"
export const products = pgTable("products", {
id: serial("id").primaryKey(),
slug: text("slug").notNull().unique(),
title: text("title").notNull(),
priceMinor: integer("price_minor").notNull(),
createdAt: timestamp("created_at").defaultNow().notNull(),
})
export type Product = typeof products.$inferSelect
export type NewProduct = typeof products.$inferInsertNo code generation, no prisma generate — everything is statically inferred.