§
§ · web development company

Custom web development, measured in milliseconds.

A full-stack web development company where performance is a contract, not a promise. Published Core Web Vitals targets, honest stack picks by problem, and code we show you before you sign.

§ 01 · an architecture, not a definition

A fintech dashboard, taken apart.

Most web development pages open with "we build websites." Here's the actual architecture we'd propose for a hypothetical 20-person fintech team needing a client dashboard, showing every decision and why. This is how we think. Stack of record: Next.js, React, TypeScript, Vercel, Cloudflare.

layer · 01

Frontend

Next.js 16 · TypeScript

Next.js over Remix because the team needs ISR for marketing pages alongside a real-time dashboard. Server Components for the data-heavy screens, Client Components only where there's interactivity. Tailwind v4 for the design system. One codebase, two rendering modes.
layer · 02

State + data

React Query · Zustand

React Query handles every server state; Zustand owns the two pieces of client state that actually warrant it (sidebar collapsed, selected account). No Redux. No Context provider tree fifteen levels deep. If a piece of state can live on the server, it lives on the server.
layer · 03

API

tRPC · Hono on Node

tRPC over REST or GraphQL because the frontend and backend share a TypeScript codebase. Type-safe end-to-end without a schema layer. Hono on Node for the occasional webhook handler that needs raw HTTP. No GraphQL because the team is 20 people, not 200.
layer · 04

Database

Postgres · Drizzle

Postgres on Neon or Supabase. Drizzle ORM because it's SQL-forward; the team reads the actual queries, not a generated abstraction. Migrations reviewed in PR. Row-level security on every multi-tenant table. We ship an EXPLAIN on every hot query.
layer · 05

Auth + billing

Clerk · Stripe

Clerk for auth because passkeys, SSO, and MFA are shipped defaults. Stripe for billing. Webhooks through a dedicated queue with idempotency keys so the same event can never double-charge. Three layers of redundancy between event and state change.
layer · 06

Infra + deploys

Vercel · GitHub Actions

Vercel for the frontend because its edge is already tuned for Next.js. GitHub Actions for CI: typecheck, tests, Playwright, Lighthouse, bundle-size guard on every PR. Preview deploys per branch. Production deploys locked behind a manual gate until we've shipped ten times.
layer · 07

Observability

Sentry · PostHog · Axiom

Sentry on day one for errors and performance. PostHog for product analytics. Axiom for structured server logs you can actually query. Core Web Vitals scraped from real users via the web-vitals package and reported weekly. You can't ship what you can't measure.

Every decision above is defensible. Every one has at least one alternative we rejected for a named reason. That's the conversation you get on day one.

§ 02 · stack picks by problem

We don't pick a favorite. We pick the right one.

Every framework is good at something and bad at something else. The decision isn't "which is best in 2026" — it's "which is best for your problem." Here's how we call it.

If your site is… Our pick
Content-heavy blog / editorial Astro
Marketing site + some interactivity Next.js 16
SaaS product with dashboards Next.js 16
Form-heavy workflow tool Remix
Shopify storefront (headless) Hydrogen
Small team, fast author velocity SvelteKit
Regulated / government / healthcare Next.js + AWS
Documentation + docs portal Astro (Starlight)
Real-time + collaborative Next.js + Partykit

We'll argue against our own pick if your situation warrants it. The call happens on the Discover week, not in the contract.

§ 03 · contract, not promise

Core Web Vitals, in writing.

Every engagement ships a Core Web Vitals contract. Three thresholds. One verification method. One penalty clause. No discovery-deck wiggle language.

largest contentful paint
2.5s

The largest visible element renders within 2.5 seconds on a throttled Moto G4 over 4G. Verified via PageSpeed Insights field data. Mobile and desktop both.

penalty: launch sprint refunded
interaction to next paint
200ms

The slowest interaction responds within 200 milliseconds. This replaced First Input Delay in March 2024. We ship under it at the 75th percentile of real users.

penalty: launch sprint refunded
cumulative layout shift
0.1

No layout shift above 0.1 in the page's lifetime. Images sized, fonts preloaded, ads and embeds reserve their space. The page doesn't jump under your finger.

penalty: launch sprint refunded
how we verify

Field data, not lab

PSI field data on mobile, 75th percentile, 28-day rolling window. Lab scores are nice; field data is the actual user experience.

Real user monitoring

web-vitals package reports every user's LCP, INP, CLS to our Axiom instance. We see regressions the same day, not the same quarter.

CI gates

Lighthouse CI runs on every PR. A score below threshold blocks the merge. Bundle-size guard does the same.

§ 04 · build or buy?

Not everything should be custom.

Most "custom" web dev is reinventing infrastructure that a hosted product already does better. Before we scope a build, we ask whether a SaaS owns the problem. Sometimes it does.

build custom if
  • The workflow is your product's competitive moat — the thing customers pay you for
  • You've tried three SaaS options and each one hits a wall your users complain about
  • Data sensitivity or compliance forbids routing through a third party
  • Performance requirements (latency, throughput, edge) exceed what any SaaS offers
  • Volume makes SaaS per-seat pricing uneconomical at 18+ months
just buy if
  • ϵYou need auth. Buy Clerk or Auth0. We will not build auth.
  • ϵYou need CMS. Buy Sanity, Contentful, or Payload. Don't build an admin panel.
  • ϵYou need billing. Buy Stripe. Buy Paddle for tax-handled SaaS.
  • ϵYou need search. Buy Algolia or Typesense. Don't write regex in production.
  • ϵYou need email. Buy Resend or Postmark. Don't configure SMTP servers.
  • ϵYou need analytics. Buy PostHog. Don't roll your own event pipeline.

The rule: buy the infrastructure. Build the thing only you can build.

§ 05 · actual code we ship

Here's what our code looks like.

A Next.js 16 server component that fetches a user and renders their dashboard with proper error handling, typed from the database all the way to JSX. No frameworks-within-frameworks, no state machines for a loading spinner.

app/(app)/dashboard/page.tsx
import { auth } from '@/lib/auth';
import { db } from '@/lib/db';
import { notFound } from 'next/navigation';
import { DashboardView } from './_components/dashboard-view';

export default async function DashboardPage() {
  // Runs on the server. Never ships to the client.
  const session = await auth();
  if (!session) return notFound();

  const user = await db.query.users.findFirst({
    where: (u, { eq }) => eq(u.id, session.userId),
    with: { accounts: true },
  });

  if (!user) return notFound();

  return <DashboardView user={user} />;
}
Fig. 1 · app/(app)/dashboard/page.tsx · Next.js 16 server component

17 lines. Authenticated. Type-safe database query via Drizzle. Graceful not-found. Zero client JavaScript for the data fetch. Zero loading spinner because Server Components don't need one. This is the baseline — not a highlight reel.

§ 06 · what we won't build

Five anti-patterns we refuse.

These show up in briefs from smart teams regularly. We'll push back on all of them — not because they're always wrong, but because they're almost always wrong for the team asking.

  1. 1.

    Micro-frontends on a single product.

    You have 20 engineers and one product. Module Federation adds weeks of infrastructure for a decoupling benefit you won't feel until you hit 200. Build a monorepo with clean module boundaries; we'll tell you when micro-frontends start paying.

  2. 2.

    GraphQL when REST or tRPC would do.

    GraphQL pays for itself when the client and server teams are separate and data needs differ wildly across consumers. If it's one frontend hitting one backend, tRPC ships end-to-end types with a tenth of the ceremony. We'll stand up GraphQL if it's genuinely warranted; we'll tell you when it's not.

  3. 3.

    Custom CMS for a 20-page marketing site.

    Sanity, Contentful, and Payload solve the problem in a week. A custom admin panel solves the same problem in three months and then requires ongoing engineering to keep alive. If your content team is two people, buy the CMS.

  4. 4.

    NoSQL without a reason.

    MongoDB is not a default. Postgres handles 99% of web-app workloads, gives you SQL, transactions, real joins, row-level security, and a hiring pool twenty times deeper. Pick NoSQL when the access pattern genuinely demands it — rarely.

  5. 5.

    Redux in 2026.

    React Query handles server state. Zustand handles the tiny slice of client state you actually have. Redux was a reasonable answer in 2017 to problems the ecosystem has since solved better. If we inherit a codebase with Redux, we don't rip it out — but we don't add to it either.

§ 07 · published rates

Four shapes of engagement.

rescue

Performance rescue

$8,000

2-week Core Web Vitals remediation. Audit, bundle tuning, image pipeline, server optimization, rendering strategy review. Fixed fee.

for · red CWV scores
landing

Single-page build

$6.5K–$18K

Conversion-focused landing or single-page. Next.js or Astro, TypeScript, analytics, full CWV contract. 2–4 weeks.

for · one high-traffic page
most picked
marketing site

Marketing or product site

$22K–$65K

5–15 pages. Headless CMS, design system wiring, i18n, schema, editorial workflows. 6–8 weeks.

for · brand at scale
application

Custom web app

$55K–$250K

Auth, dashboards, workflows, integrations, real-time, billing. Next.js + Node or full-stack TypeScript. 10–20 weeks.

for · product, not page

Prices valid through 2026-Q3. Scope changes get priced before they happen, not after.

§ 08 · questions

Six answers.

What does a web development company actually do?

A custom web development company engineers sites and web applications end-to-end: architecture, backend APIs, database schema, frontend framework, rendering, auth, integrations, CI/CD, deployment, and post-launch optimization. At Digital Heroes, every engagement commits to a Core Web Vitals contract (LCP < 2.5s, INP < 200ms, CLS < 0.1) before the first invoice. Miss the target, the launch sprint is refunded.

How much does custom web development cost?

Landing or single-page: $6,500–$18,000. Marketing or product sites (5–15 pages + CMS): $22,000–$65,000. Custom web applications (auth, dashboards, real-time, billing): $55,000–$250,000. Performance rescue: $8,000. Ranges are published; scope moves price, discovery doesn't.

Which web framework should I use in 2026?

Follow the problem, not a favorite. Next.js 16 for content-plus-app sites. Astro for content-heavy where JS should be zero. Remix for form-heavy data-forward apps. SvelteKit for small teams valuing ergonomics. See the decision matrix in § 02 above.

Do you publish Core Web Vitals targets?

Yes, and contractual. LCP < 2.5s, INP < 200ms, CLS < 0.1, verified on throttled Moto G4 via PageSpeed Insights field data. If the launch build misses any of the three, the launch sprint is refunded at the client's option.

Can you rebuild our existing site?

Yes. Rebuild starts with a technical audit (dependencies, rendering strategy, bundle size, queries, CMS friction, CI/CD maturity). We publish the audit before we touch code. Rebuilds typically run 6–12 weeks with preserved URLs, 301 redirects, and Search Console address change. Rankings are almost always preserved.

Will you work with our existing engineering team?

Yes. Three shapes: white-label (we ship, your team brands), embedded (our engineers in your sprints, 3-month minimum), handoff (we build, you maintain). Code quality and documentation are the same across all three — we assume your team will read every line.

ready to ship

Start with a metric.

Tell us the number that's broken — conversion, TTFB, bounce rate, user-reported latency. We'll come back with an architecture, a stack, a performance contract, and a price. Within 24 hours. No discovery calls.