Building Modern Web Applications — The Stack That Actually Ships

The 2026 web stack is not 'pick a meta-framework and go.' It is a small set of unglamorous architectural decisions that determine whether your app is still alive in 2028. Here is the stack I ship every project on, and the seven decisions behind it.

Published
10 January 2024
Updated
16 May 2026
Read time
10 min
Words
1,778
Tags
nextjs · ai · web
Building Modern Web Applications — The Stack That Actually Ships — cover
Building Modern Web Applications — The Stack That Actually ShipsEngineering

There is a 2024 version of this article. I wrote it. It is bad. It told you to pick Next.js and integrate AI, with a code snippet using text-davinci-003 (an OpenAI model that has been deprecated for over a year). Reading it back is humbling.

This is the rewrite. Not because the framework changed — Next.js is still the right default for most React projects in 2026 — but because the architectural decisions underneath the framework choice matter more than the framework choice itself. This is the stack I actually ship every project on, and the seven decisions behind it.

The 2026 web stack is unglamorous and that is the point

The defining characteristic of the 2026 web stack is that the interesting decisions are not in the framework. The framework is mostly settled — Next.js, Astro, Remix, SvelteKit, or a few others depending on your use case. The decisions that determine whether your application is still healthy in 2028 are about:

  • What you depend on (Postgres? Supabase? Firebase?)
  • Who owns your data (your DB or someone else's SaaS)
  • What is server-rendered vs. client-rendered
  • Where AI integration lives (and where it does not)
  • How you handle auth, payments, and transactional state

These are boring questions. Almost nobody markets around them. They are 90% of the engineering work.

The web stack is no longer a framework choice. It is a series of architectural choices that the framework only loosely constrains.

BUILD — three stacked horizontal bars labeled DATA, API, UI with the middle API bar filled in violet
The stack is three layers. The middle layer is where the engineering judgment lives.

The stack I ship every project on (April 2026)

For ~85% of projects I scope today, this is the stack. Substitutions exist for specific needs (Astro for content-only sites, FastAPI for Python-shop backends), but the defaults are unchanged for the last 18 months:

LayerChoiceWhy
FrameworkNext.js 16 (App Router)Best React server-side story, mature deployment story
LanguageTypeScript (strict)Type safety is non-negotiable above one developer
StylingTailwind CSS v4Utility-first works, but kept to layout — branding is in @layer components
UI primitivesRadix headless + custom CSSI avoid pre-styled UI kits; brand needs are too specific
DatabasePostgreSQL (Neon for serverless)Boring, fast, supported everywhere
ORMDrizzleType-safe queries, no magic, transparent SQL
Authiron-session for simple sites; Auth.js for multi-providerSkip the auth-as-a-service layer unless you need social login at launch
PaymentsStripePeriod. The fintech complexity is the moat.
EmailNodemailer + SMTP (Websupport/Postmark)Avoid Resend/SendGrid lock-in on transactional
HostingVercel (preferred) or Fly.ioChoose based on whether you need WebSocket or workers
Object storageCloudflare R2 (S3 API)10× cheaper than S3 with no egress fees
AI layerAnthropic Claude API for production, fallback to GeminiSee § AI integration below

That is the whole stack. If a project does not fit this, it is usually for a specific reason I can name (regulatory, real-time, niche language requirements). Default to boring. Optimise only where the project genuinely needs it.

STACK — vertical architecture cross-section showing EDGE / APP / DATA / INFRA layers with diagonal hatching, APP layer highlighted in violet
The 2026 stack as cross-section. Four layers, one per concern. The interesting engineering decisions live in the APP layer.

The seven decisions that actually matter

1 · Server-first rendering by default

In 2026, marking every component 'use client' is a sign you do not understand the platform. Server Components are the default for a reason. They:

  • Reduce JavaScript bundle size (the only thing that consistently matters for page-load performance)
  • Centralize data access in one tier (the server) where auth, caching, and rate-limiting actually work
  • Let you use database queries inside components without a separate API route layer

Client components are still necessary for interactivity (forms, state, animations, browser APIs). The discipline is to make them small and leaf-level — not to wrap the whole page in 'use client' because one button needs onClick.

2 · One database, one ORM, no abstractions on top

A surprisingly common 2024 pattern was: pick a database, then put a GraphQL layer on top, then put a SaaS BaaS layer on top, then add a feature-flag service on top, then realise half your stack is rented. The simpler 2026 pattern is: Postgres + Drizzle, with the application code talking to the database directly through type-safe queries.

This is not a religious position. GraphQL exists because someone built it for a real problem (mobile apps consuming inflexible REST APIs). For a web app where the frontend and backend live in the same codebase, GraphQL adds infrastructure without solving a problem you have.

3 · Tailwind for layout, CSS for branding

Tailwind utility classes are excellent for layout, spacing, and basic typography. They are bad for components with brand-specific styling. The compromise is the split I use in every project:

  • Tailwind utilities in JSX for layout: flex, grid, gap-*, px-*, breakpoints
  • Plain CSS (Modules or @layer components) for brand styling: anything with hover states, transitions, brand colours, or component-internal logic

Without this split you end up with 12 Tailwind classes per element, your JSX becomes unreadable, and your design system effectively lives in the markup. The split keeps the code clean and the design system intentional.

4 · Auth is a build-it problem, not a buy-it problem

For a single-user admin or a 10-user team, iron-session is 30 lines of code and good enough forever. For multi-provider OAuth, Auth.js (formerly NextAuth) is mature and well-supported. For enterprise SSO, Clerk or Auth0 is appropriate.

What is rarely appropriate is buying auth-as-a-service for a small product that just needs email/password. You will pay $50–$500/month for a feature that took an afternoon to build. The vendor will change their pricing in year two.

5 · Drizzle for the ORM, not Prisma

This is the most controversial item in the list, so I will be direct. I shipped Prisma for three years and stopped 18 months ago for two reasons:

  • Build performance. Prisma's code generation is slow and gets slower as your schema grows. On a project with 60+ tables, the dev loop became painful.
  • Query opacity. Prisma generates SQL you cannot fully see or control. For 80% of queries this is fine. For the 20% where performance matters, you are debugging a black box.

Drizzle solves both. It is essentially a typed SQL builder. The queries you write look like SQL because they are SQL with a thin TypeScript layer. The migrations are explicit. The performance is transparent. Switch happened in 18 months across my client work and I have not looked back.

6 · AI integration where it moves a measurable metric

This is where most teams in 2026 are wasting money. Marketing pressure pushes companies to add "AI features" to everything. The result is decorative AI that does not improve any user-facing metric.

The right pattern is to identify one or two functions where AI actually moves a measurable metric:

  • Search ranking (relevance scores improve with semantic embedding)
  • Content classification (auto-tagging support tickets, routing)
  • Draft generation (email replies, product descriptions, summaries)
  • Anomaly detection (suspicious orders, fraud signals)

Then implement those carefully, with a fallback path when the AI is wrong, and instrumentation that shows the metric moving. Skip "add AI to everything" — it is the 2026 equivalent of "add blockchain" from 2018.

Need help scoping where AI actually belongs in your product? That is a discovery sprint conversation.

CACHE — vertical memory tier stack L1 CPU / L2 RAM / L3 REDIS / L4 DISK, dark-to-violet gradient background, L3 REDIS highlighted white
The boring infrastructure choices that pay back in year three. L3 REDIS doing the heavy lifting is not glamorous. It is right.

7 · Boring infrastructure for everything that ships money

This is a hard-earned lesson. For anything that touches money, identity, or compliance, use boring infrastructure with long track records.

  • Payments: Stripe. Period. Their fintech complexity is what you are buying.
  • Auth tokens: iron-session or Auth.js. Both are battle-tested.
  • Email: SMTP via a deliverability-focused provider (Postmark, SES). Avoid trendy "modern email" SaaS for transactional unless you have a specific reason.
  • Database: Postgres. Not because it is the best, but because it has 25 years of bug fixes and every senior engineer in the world has used it.

The trendy alternative is always 30% faster and 80% less proven. For a side project, fine. For your business, no.

Case study — small e-commerce on this stack

Concrete example. A small e-commerce platform I shipped last quarter, illustrating the choices in production.

The brief: Slovak family-run sporting goods retailer, ~120 SKUs, expanding from physical store to online. Budget €20,000 for the build, 12 weeks. Required: Slovak/English bilingual, custom product configurator for skis (length + binding + boots combinations), Stripe payments, Slovak invoicing integration (Pohoda/Money S3).

The stack chosen:

  • Next.js 16 App Router for the storefront
  • Drizzle + Postgres (Neon) for product catalog and orders
  • Tailwind for layout + custom CSS for the ski configurator UI (interactive, needed proper state and animation)
  • Stripe Connect for payments, integrated with the Slovak invoicing API
  • Cloudflare R2 for product images
  • Nodemailer + Postmark for order confirmations
  • Lightweight semantic search using OpenAI embeddings for product search (the one piece of AI in the build)

The results after 90 days:

  • 99.97% uptime measured by external monitor
  • p95 page load 0.9 seconds (Core Web Vitals all green)
  • Search-to-purchase conversion 5.2% (industry average for sporting goods sits at 2–3%)
  • €0 in unplanned infrastructure costs — total hosting bill ran €34/month on Vercel + Neon free tier through launch volume

The unsexy answer to "what enabled the result" is: no special tricks. Boring stack chosen carefully, executed well, instrumented enough to know what works.

Takeaways — your 2026 web stack checklist

  • Default to Next.js 16 + Server Components. Mark 'use client' only when you must.
  • Postgres + Drizzle. Skip the ORM that ships its own runtime.
  • Tailwind for layout, plain CSS for branding. Stop putting design tokens in className strings.
  • Buy auth only if you need OAuth or enterprise SSO. Otherwise iron-session is enough.
  • Stripe for payments. Postmark or SMTP for email. R2 for storage. Boring is the point.
  • AI where it moves a metric you can measure. Not as decoration.
  • One major version behind the bleeding edge. Stability over novelty.

The 2026 web stack is not a framework. It is a sequence of architectural decisions that respect both engineering reality and business reality. Pick boring infrastructure, ship the actual work, instrument to know what works.


Related: Custom Web Application Development Cost in 2026 · Owning Your Stack in 2026 · How I Run Discovery Sprints

Share this essayPost on XShare on LinkedIn
Norbert Kovalčín
Written by Norbert KovalčínIndependent architect · Europe · CETI help companies own their stack instead of renting it. One client at a time.
Enjoyed this?

New essay every few weeks.

Subscribe for the next one. Double opt-in, unsubscribe in one click, no tracking pixels.