§
§ · react development

React development, past hydration.

A Server-Components-first React practice for SaaS products, content platforms, and client portals. Next.js 16 App Router, Suspense streaming, and the sharp server-client boundary modern React actually runs on.

§ 01 · react has three jobs now

Every component is doing one of three jobs.

Modern React is no longer one thing. In 2026 every component is one of three shapes: a Server Component that renders on the server and ships zero JS, a Client Component that hydrates in the browser for interactivity, or a Server Action that runs on the server but is called from the client. Knowing which shape each component is doing is the central architectural discipline. Codebases that treat every component the same way end up with hydration taxes they cannot pay and bundle sizes they cannot explain.

job 01 · server components

Data + structure, zero JS.

Render on the server, stream HTML to the client, fetch data inline. Default for content, layouts, and anything read-only. The single biggest bundle-size win since tree-shaking.

job 02 · client components

Interactivity, costs JS.

Marked with use client. Hydrate in the browser; handle state, effects, events. The surface area of client components is your bundle size.

job 03 · server actions

Mutations, type-safe.

Async functions marked use server. Run on server, called from client. Replace REST mutation endpoints. Type-safe end-to-end, no manual API layer.

§ 02 · framework decision

Next.js, Remix, or Vite SPA. Pick by shape, not by brand.

Three React frameworks cover almost every modern project. Next.js 16 App Router is our default because it is where React Server Components live as a first-class concept. Remix wins when framework-agnostic routing and nested error boundaries are the architectural requirement. Vite SPA is acceptable only for pure client-side dashboards with no SEO and no initial-load performance constraints.

 Vite SPANext.js 16 App RouterRemix
renderingclient onlyRSC + SSR + ISR + staticSSR + streaming + optional RSC
data fetchingclient-side via useEffect / SWR / TanStackServer Components fetch inlineloaders (server-side) + actions
bundle sizelargest (all code ships)smallest (RSC ships zero JS)smaller than SPA, larger than RSC
SEO + CWVweak; hydration taxbest available for Reactstrong
best forinternal dashboards, auth-gated80% of modern buildsform-heavy platforms, nested routing
§ 03 · the server-client boundary, in motion

One tree. Two runtimes.

The hardest thing about modern React is knowing where the server stops and the client begins. The animation below draws the boundary on a typical component tree: everything above the amber line renders on the server and streams as HTML; everything below hydrates on the client.

Fig. 1 · the boundary · where hydration happens
§ 04 · rsc + suspense patterns

Four patterns cover most modern React.

Editorial React component tree diagram showing a page.tsx parent node branching to header, content, and footer children, with content branching further into ArticleBody and InteractiveWidget (marked use client), and an amber dashed horizontal line cutting across marking the hydration boundary.
Fig. 2 · one tree, two runtimes · where hydration happens
pattern 01 · server-first data

Fetch in the component.

Server components fetch data directly inline. No useEffect. No loading state passed through props. No SWR wrapper. Data fetching lives in the component that renders it.

pattern 02 · client islands

Small, interactive.

Client components are islands inside server pages: the comment input, the cart button, the modal trigger. The rest stays server-side. Bundle stays small.

pattern 03 · suspense streaming

Parallel fetches, progressive render.

Wrap slow parts of the page in Suspense. Fast parts render first, slow parts stream in as data arrives. Users see meaningful content in hundreds of milliseconds, not seconds.

pattern 04 · server actions

Typed mutations.

Forms, updates, deletes via Server Actions. No REST endpoint. No tRPC router. Type-safe from form submit to database. The write path most React apps have been missing.

§ 05 · state management

Most state is not client state anymore.

RSC plus Server Actions eliminate most of what client state was used for. Remote data, form submission state, cache invalidation all move server-side. What remains is genuinely client-only: UI state, derived values, cross-component ephemeral state. The decision matrix below is what we use on new builds.

what you needwhat we reach forwhy
simple UI stateuseStateno library, no overhead
shared UI state in a subtreeuseContextcolocated, explicit
cross-component client storeZustandsmall, typed, no boilerplate
atomic fine-grained reactivityJotaiwhen component re-renders must be surgical
remote data (client-fetched only)TanStack Querycache, refetch, invalidate done right
form stateReact Hook Form + Zodvalidated, typed, performant
§ 06 · typescript + testing

Strict typing and three test layers. No exceptions.

typescript discipline
  • strict: true on day one; noUncheckedIndexedAccess on day two
  • No any; no unknown cast without runtime validation
  • Zod on every API boundary; generated types from DB schema
  • Branded types for IDs and sensitive primitives
testing layers
  • Vitest for unit tests on pure logic, 90%+ on critical utils
  • React Testing Library for component behavior, user-visible only
  • Playwright for E2E on critical journeys (signup, primary conversion)
  • MSW for HTTP mocking; CI runs on PRs, deploy blocked on red
§ 07 · performance SLA

The numbers we hold ourselves to on Next.js 16.

templateLCPINPCLSJS bundle (gzip)
marketing page< 1.6s< 150ms< 0.05< 40 KB
content / article< 1.8s< 150ms< 0.05< 60 KB
authenticated dashboard< 2.2s< 180ms< 0.08< 140 KB
form / checkout< 2.0s< 180ms< 0.08< 100 KB
§ 08 · proof

Two archetypes. One built, one migrated.

archetype 01 · SaaS product, $2M ARR

Next.js 16 from scratch.

Vertical SaaS with authenticated dashboard, public marketing, Stripe billing. Server Components throughout, client islands for editor and chart interactivity. Ship to Vercel edge, Postgres on Neon. 14 weeks.

LCP marketing
1.2s
mobile p90
JS bundle
38 KB
marketing pages
test cov
78%
critical paths 94%
archetype 02 · content platform, 4M pageviews/mo

Pages Router → App Router migration.

Large content platform on Next.js 12 Pages Router. Migrated route by route to App Router with Server Components. 18-week migration, no traffic drop during transition. 62 percent bundle size reduction on content pages.

bundle cut
-62%
content pages
LCP win
-1.1s
mobile p75
traffic
stable
zero drop
§ 09 · engagement shapes + fit

Four shapes. Audit names the right one.

shape 01

Architecture audit.

2 weeks. Boundary + bundle + state review. Refundable. Contact for quote.

shape 02

New build.

10-18 weeks. Next.js 16 from scratch. Contact for quote.

shape 03

SPA migration.

12-20 weeks. SPA / Pages Router → App Router. Contact for quote.

shape 04

Retainer.

20+ hrs/week named engineers. Month to month. Contact for quote.

right fit
  • SaaS, content platform, or client portal on React
  • You want Server Components and App Router as the default
  • TypeScript strict-mode and 70%+ test coverage are non-negotiable
  • You can own the codebase after we hand off
wrong fit
  • ×You want to stay on CRA or Redux; we will not ship that stack in new builds
  • ×No TypeScript anywhere in the codebase and no plan to adopt it
  • ×You are a content site; consider WordPress or Webflow
  • ×You want tomorrow; we do not compress quality
§ 10 · questions

Eight answers.

Should we still use Create React App or Vite SPA in 2026?

No for new projects. CRA has been deprecated since 2023 and Vite SPA is a good development tool but the wrong production default. For a new React project in 2026, start with Next.js 16 App Router or Remix if you need framework-agnostic routing. Vite + React Router is acceptable only when the app is a pure interactive dashboard with no SEO surface, no initial-load performance requirements, and no server-side rendering needs. In practice, that is under 15 percent of briefs.

What's the difference between Server Components and SSR?

SSR (Server-Side Rendering) renders the full React tree on the server, ships HTML plus the full JavaScript bundle, and hydrates everything on the client. Server Components render on the server and ship zero JavaScript for the server-rendered parts; only Client Components ship JS. The result is dramatically smaller bundles, faster Time to Interactive, and a natural way to fetch data close to where it's rendered. SSR was about when to render; Server Components are about what to ship.

How much does custom React development cost in 2026?

US and UK senior React engineers are $150 to $210 per hour at boutique agencies; Indian mid-market senior engineers are $95 to $150 per hour. A Next.js 16 App Router build runs 10 to 18 weeks depending on scope. A migration from SPA or Pages Router to App Router runs 12 to 20 weeks. A 2-week architecture audit on an existing codebase is refundable against any engagement that follows. Retainer engagements start at 20 hours per week of named engineer time. Scoped quote in 48 hours.

Do we need to rewrite everything to use Server Components?

No, and you probably should not. Next.js App Router allows incremental adoption: new routes on App Router, legacy routes on Pages Router side by side. We typically migrate route by route, starting with the highest-traffic content pages (biggest bundle-size win from Server Components) and leaving interactive dashboards on the existing router until the migration naturally reaches them. A full codebase rewrite is the wrong default; the right default is a planned 4 to 8 month incremental migration with a per-route test.

Which state management library do you recommend?

By default none. React Server Components plus Server Actions eliminate most of what client state was used for: remote data, form state, cache invalidation. What remains is genuinely client-only state: UI state (modals, drawers, tabs), derived values, and cross-component ephemeral state. For that, useState and useContext are the right default. If you need more, Zustand for small stores, Jotai for atomic fine-grained reactivity, TanStack Query for any data still fetched client-side. We avoid Redux in new builds; its conceptual weight does not match the modern React runtime.

How do you handle TypeScript strictness?

strict true on day one, noUncheckedIndexedAccess true on day two, exactOptionalPropertyTypes true when the codebase is clean enough to absorb it. No any. No unknown cast without runtime validation. Zod or Valibot on every API boundary. We generate TypeScript types from the database schema (Prisma, Drizzle, or Kysely) and from tRPC or Server Actions so the frontend always knows what the backend returns without manual type definitions. Strict TypeScript on React pays back its setup cost in the first month of maintenance.

What's your testing approach for React?

Three layers. Vitest for unit tests on pure functions, utility modules, and component logic. React Testing Library for component behavior tests, focused on what the user sees and interacts with, not implementation details. Playwright for end-to-end tests on critical user journeys (signup, checkout, primary conversion flows). MSW for mocking HTTP at the service-worker level so tests are isolated. Coverage target is 70 percent overall, 90 percent on critical paths. We do not chase 100 percent coverage; it is an anti-pattern.

Who owns the code, hosting, and deployment on exit?

You do. Private GitHub repository under your organization from day one. Deployment on your Vercel, Netlify, Cloudflare Pages, or AWS account. Domain on your registrar. Third-party services (Sentry, Clerk, Stripe) under your billing. DH engineers get named access, removable any time. On exit we transfer the repository, document the deployment pipeline, record a Loom walkthrough of the architecture, and leave a 30-day support tail. No lock-in through code, config, or vendor choice.

Start with an architecture audit.

Two weeks. Boundary review, bundle analysis, state management, upgrade path. Refundable against any engagement that follows. Scoped quote in 48 hours.