Craftkitdocs

Tech stack

Languages, frameworks, runtime choices.

03 — Tech Stack

Locked recommendations for v0.1 / v0.2. Each choice has a documented reason and a fallback — we don't change them on a whim.

Runtime & language

Layer Choice Rationale
Language TypeScript 5.x (strict) Shared types end-to-end
Runtime Node 22 LTS Native fetch, native test runner, ES modules
Package manager pnpm 9 Faster than npm, content-addressable store, strict deps
Monorepo Turborepo Caching, pipeline graph, zero config
Lint + format Biome One tool, faster than ESLint + Prettier

Frontend

Layer Choice Rationale
Framework Next.js 15 (App Router) Server actions + RSC + edge-friendly
React 19 Concurrent + Suspense, async transitions
Styling Tailwind v4 Native CSS variables, design-token first
Components shadcn/ui Owned source, no runtime cost
Editor Tiptap v2 Best-in-class extensibility on ProseMirror
Forms React Hook Form + Zod Same Zod schemas as the API
State RSC + URL state, then Zustand for client islands Avoid global stores

Backend

Layer Choice Rationale
HTTP Next.js Route Handlers for public API Same runtime as dashboard
Internal RPC tRPC (server actions) Type-safe dashboard mutations
ORM Drizzle Lightweight, SQL-first, great types
DB Postgres 16 Versioned templates need relational integrity
Auth better-auth Self-hostable, owns its tables, no vendor lock
Queue BullMQ + Redis 7 Battle-tested for async jobs
Templating Handlebars Mature, safe, supports {{#if}} / {{#each}}
Validation Zod + auto-generated JSON Schema One source of truth

Document rendering

Layer Choice Rationale
PDF engine Puppeteer + bundled Chromium Full CSS fidelity
Serverless variant @sparticuz/chromium Compatible with Lambda/Vercel functions if needed
HTML templating Handlebars with safe helpers format-date, format-currency, eq, gt, lt, each
Storage SDK @aws-sdk/client-s3 S3-compatible against MinIO (dev) / R2 (prod)

Embed mode (v0.2)

Layer Choice Rationale
JWT signing EdDSA (Ed25519) Asymmetric, fast, modern
JWT library jose Standards-compliant, well-maintained
postMessage protocol hand-rolled in @craftkit/embed Tight control, no transitive deps
Iframe sandbox allow-scripts allow-forms allow-same-origin Narrowest viable

AI (v0.2 capstone)

Layer Choice Rationale
Provider abstraction Vercel AI SDK Multi-provider, streaming, structured output
Default models OpenAI gpt-4.1-mini for fast, gpt-4.1 for high quality Stable cost/quality
Fallback Anthropic claude-sonnet-4 Diversification
Output format Tiptap JSON via structured-output LLM-cannot-corrupt invariant

Observability

Layer Choice Rationale
Errors Sentry Standard, generous free tier
Product analytics PostHog (self-hostable) EU-friendly, owns data
Logs Axiom or Better Stack Cheaper than Datadog at our scale
Metrics OpenTelemetry Vendor-neutral

Testing

Kind Tool
Unit Vitest
Component Vitest + Testing Library
E2E Playwright
API contracts Vitest with real DB (Docker)

CI/CD

  • GitHub Actions for CI
  • Per-PR: typecheck, lint, unit, anti-residue scan
  • Per-merge to main: integration tests, deploy preview
  • Per-release tag: production deploy to Vercel + Railway

What we deliberately did NOT choose

Rejected Why
Express / Fastify Next.js Route Handlers cover it without a second runtime
Mongo / DynamoDB Versioned templates + audit trails want relational
MJML for emails Tiptap-based email blocks = same builder for everything
Clerk Vendor lock; better-auth gives the same DX self-hostable
Mux / Cloudinary Out of scope for v0.1–v0.3
Prisma Heavier than Drizzle, slower codegen
ESLint + Prettier Biome is one tool, faster, less config

Last revised: 2026-05-02