Back to blog
engineering

Our TypeScript Monorepo Stack in 2026

A walkthrough of the tools and patterns powering our monorepo: Turborepo, pnpm workspaces, Biome, and how they all fit together.

Shawn OlsonApril 5, 20262 min read

After years of managing separate repos for frontend, backend, and shared packages, we went all-in on a monorepo. Here's the stack and why each piece earns its place.

The Foundation: pnpm + Turborepo

pnpm workspaces handle dependency management. Strict hoisting, efficient disk usage, and workspace:* protocol for internal packages. It just works.

Turborepo orchestrates builds, type-checking, and linting across packages. The remote cache means CI runs are fast — usually under 30 seconds for a full pipeline.

Structure

apps/
  web/       # Next.js 15 marketing site
  platform/  # Vite + React 19 SPA
  api/       # Hono REST API
packages/
  ui/        # Shared component library
  db/        # Drizzle ORM schema + client
  types/     # Shared TypeScript types + Zod schemas

Each app has its own build tooling. The marketing site uses Next.js for SSG/SSR. The platform is a Vite SPA with TanStack Router. The API runs on Hono with Node.js.

Biome Over ESLint + Prettier

We replaced ESLint and Prettier with Biome. One tool handles formatting and linting, and it's an order of magnitude faster. Single biome.json at the root, consistent rules everywhere.

Drizzle ORM for Database

Drizzle gives us type-safe SQL without the magic. Schema defined in TypeScript, migrations generated automatically, and the query builder follows SQL patterns we already know.

What We'd Do Differently

If starting today, we'd probably evaluate Bun as the runtime. The tooling ecosystem has caught up, and the performance gains for development are real.

We'd also invest in E2E tests earlier. Playwright was a late addition, and we spent time debugging integration issues that tests would have caught.

Takeaway

A monorepo isn't automatically better — it's better when your packages genuinely share code and your team is small enough to manage it. For us with 3 apps sharing a component library, database layer, and type definitions, the overhead pays for itself.