THE DESCENT · what happened to the frontend

The last time you wrote a frontend,
it looked like one of these —

server-rendered · 2009
<?php foreach ($cart as $item): ?>
  <li>
    <?= $item->name ?>
    <button>Buy</button>
  </li>
<?php endforeach; ?>
or just hand-written
<!-- styled by hand, no build -->
<button class="buy">Buy</button>

<style>
  .buy {
    padding: 4px 10px;
    border: 1px solid #999;
  }
</style>
either way: 0 build steps · 0 dependencies
worked in every browser, forever

Then you looked away.
What the hell happened?

Two decades, forty-four meters of build tools, and one very confused <button> later, here's the whole story. Spare twenty minutes; I'll get you up to speed.

Begin the descent

In 2008 you saved a file called index.html, dragged it onto an FTP client, and watched a little progress bar crawl to the right. When it finished, your website existed. It worked in Internet Explorer and Firefox and that new one called Chrome. You did not run a "build." You did not install 1,400 packages. You wrote the markup, you wrote the styles, and the browser did exactly what you told it.

You were good at this. Then, somewhere between the launch of the iPhone and now, you blinked, and the entire discipline rebuilt itself from the studs while you were busy shipping actual products. Now a beginner's tutorial opens with sixteen tools you've never heard of, half of them named after Japanese words for "fast," and the first command downloads more code than the Apollo guidance computer ran on, just to render a contact form.

Here is the good news, and it's the thesis of this whole descent: none of it is arbitrary. Every tool you're about to meet is scar tissue grown over a real wound. Somebody hit a genuine problem, built a fix, and the fix created the next problem, which got its own fix, and the sum of two decades of reasonable steps is the magnificent, exhausting cathedral of madness you're staring up at today.

So we won't memorize a list. We'll dig, in order, and let each tool show up exactly when the pain that justifies it shows up. We'll keep checking on one specimen the whole way down, a single humble <button>, and watch what the industry does to it. And when we hit bedrock, you're going to laugh, because the frontier of 2026 looks an awful lot like the file you uploaded over FTP.

Every tool is scar tissue over a real wound. Follow the wounds, and the map draws itself. The one idea that unlocks everything
LAYER I — 4 M 2006 – 2010

The Itch

"I just want to change the page without reloading the whole thing."

The first wound was small and reasonable. You wanted a part of the page to change, a menu to open, a form to validate, a section to load, without the white blink of a full page reload. The browser could do this through a gnarly thing called XMLHttpRequest and a pile of fussy DOM methods that worked differently in every browser.

So the era reached for jQuery, and it was genuinely great. $("#cart").load(...) and the differences between browsers melted away. AJAX (fetching data without a reload) went mainstream. For a few golden years, this was enough.

But applications got ambitious, and a deeper wound opened underneath. When your data lived in JavaScript variables and on the screen, you were the synchronization machine. Change the price? Better remember to hand-update the cart total, the header badge, the checkout button, and that little summary box. Miss one and the UI lies to the user. This manual DOM-poking, by hand, across a growing app, is the original sin that everything after this layer is trying to atone for.

The artifacts — jQuery still runs a huge share of the web; nobody starts new projects with it
LAYER II — 9 M 2010 – 2015

The Frameworks Arrive

"Stop making me sync the screen to my data by hand."

The fix for the synchronization wound was a genuinely big idea: declarative UI. Instead of writing the steps to update the page ("find the badge, set its text"), you describe what the page should look like for any given data, and the framework figures out the steps. You stopped touching the DOM. You changed the data; the screen followed. This is the meaning of the word reactive you keep seeing.

The unit of construction became the component: a self-contained chunk of UI (its markup, its behavior, its bit of state) that you compose like LEGO. React, out of Facebook in 2013, won the era by pairing components with JSX (an HTML-looking syntax written inside JavaScript, which still unsettles people) and a clever trick called the Virtual DOM: keep a lightweight copy of the page in memory, diff it on every change, and touch only the real DOM nodes that actually differ.

Then a thousand flowers bloomed. Vue made it approachable. Angular (the grown-up rewrite, not the dead 2010 "AngularJS") brought enterprise structure and TypeScript. Svelte asked why ship a framework to the browser at all, and compiled itself away. Solid kept JSX but threw out the Virtual DOM for surgical updates. The thing they're all converging on now is the signal, a value that knows who depends on it and updates exactly those, and nothing else.

Specimen 01 — the button becomes a component · 2013 layers: 6
// it's not markup anymore. it's JavaScript
// that pretends to be markup.

function Buy() {
  return (
    <button onClick={addToCart}>
      Buy
    </button>
  )
}
The button now has state, props, and a parent tree. It cannot exist without a framework runtime in the browser. To see it, you'll soon need a build step — keep digging.
source: ~140 bytesships with: React runtime ~45 KBbuild steps: 1 (and counting)
The component frameworks — React is dominant; the rest are very much alive
The dinosaur's escape hatch

Not everyone took this road. A stubborn, growing camp, htmx, Alpine.js, Rails' Hotwire, says the original sin was leaving the server at all, and brings HTML-over-the-wire back. If this layer makes you homesick, that camp is built for you, and we'll return to it at bedrock.

LAYER III — 15 M 2012 – 2018

The Build Step

"Why can't I just open the file anymore?"

This is the layer you resent most, so let's be honest about why it exists. Once JavaScript was building the whole page, two facts collided. First, the language had no real way to split code across files, no import, for most of its life, so the community invented competing module systems (CommonJS's require in Node, then the official ES Modules import) that to this day don't fully agree with each other. Second, you wanted to write modern syntax and JSX that old browsers couldn't understand.

So a translation layer appeared. Babel transpiles tomorrow's JavaScript (and JSX) into yesterday's so any browser can run it. A bundler (webpack was king) stitches your hundreds of little files into a few, because under the old HTTP, hundreds of requests were slow. Along the way it minifies (strips whitespace), tree-shakes (drops code you never use), splits the result into chunks, and emits source maps so you can still debug the original. That bundle of jobs, collectively, is "the build."

And the cost? node_modules: the folder where your dependencies, and their dependencies, and their dependencies live. A blank starter project routinely lands a quarter-million files. It is the heaviest object in the universe, and it's sitting in your projects folder right now.

Your 22-byte button now arrives in a 2-megabyte bundle, assembled by a config file you did not write and cannot read. Layer III · the build, summarized
The build machinery — the struck-through ones are fossils you'll still see in old job posts
LAYER IV — 21 M 2018 – 2024

The Tooling Arms Race

"Fine, there's a build. Just make it stop taking ninety seconds."

The build worked, but it was slow, and webpack configs had become a dark art passed between developers like cursed scrolls. The next wound was purely about pain: the tools themselves were too miserable to use. The fix was to rewrite them in fast, compiled languages, Go and Rust, instead of JavaScript.

esbuild (Go) made bundling 10–100× faster and embarrassed everyone. SWC (Rust) replaced Babel inside the big frameworks. And Vite tied it together into the tool most people now reach for by default: instant startup in development (it leans on your browser's native modules and esbuild), a proper optimized build for production, and HMR, hot module replacement, so you save a file and see the change without losing your place. As of 2026 Vite's production bundler is itself being replaced by a Rust one, Rolldown, and the whole stack is consolidating under one company, VoidZero (which Cloudflare just acquired).

The same "rewrite it for speed" wave hit package managers (pnpm with its clever shared store, Bun the all-in-one) and bundlers for the framework crowd (Turbopack, Rspack). If a single sentence captures this layer, it's the one every conference echoed: "we rewrote it in Rust, and it's 50× faster."

The speed era — almost everything here is written in Rust or Go; that's the whole point
LAYER V — 28 M 2014 – 2026

The Server Homecoming

"My beautiful app shows a blank white screen and Google can't see it."

By now the all-JavaScript app had a problem it couldn't dodge: the server sent an empty <div> and a mountain of JS, so the user stared at a blank white screen until it all downloaded and ran, and search engines, looking at that empty div, saw a blank page too. For a content business, that's fatal. (Google even put a number on it, the Core Web Vitals, so slowness now cost you rankings.)

The fix was to render the HTML on the server again. Which is to say: the exact thing you were doing in 2008. This is where the acronyms breed, so here's the whole family in one breath. SSR renders the page on the server per request. SSG pre-renders it once at build time (your old static site, with tooling). ISR is SSG that quietly refreshes itself. And the frameworks that orchestrate all this, the meta-frameworks, are the layer most people now actually start from: Next.js (React), Astro (content-first, ships almost no JS), SvelteKit, Nuxt (Vue), Remix (now merged into React Router).

But sending server HTML to a framework built for the browser created the signature wound of the modern era: hydration.

STEP 1

The server cooks

It runs your components and ships finished HTML. The page paints instantly. It looks done.

STEP 2

But it's dead

None of the buttons work yet. The HTML is a photograph of the UI, not the UI. No handlers are attached.

STEP 3

So you pay again

The browser downloads all the JS and re-runs the same app to "wake it up." For a moment, users rage-click a button that looks alive and isn't.

Hydration is paying for the meal twice: once to cook it on the server, once to cook the identical meal in the browser to prove it's edible. Layer V · the hydration tax

Everything fashionable after this is a scheme to hydrate less. Islands (Astro) keep the page static and hydrate only the few interactive bits. Resumability (Qwik) skips hydration entirely. And React Server Components (RSC), the most confusing idea in modern React, let components run only on the server and ship zero JavaScript to the browser, with a "use client" line marking the interactive leaves. It's genuinely clever and genuinely hard, and if one acronym tells you the ground moved while you were gone, it's this one.

The meta-frameworks — Next and Astro lead; Gatsby is fading
LAYER VI — 34 M 2015 – 2026

The Grown-up Bolt-ons

"This codebase is unmaintainable and I keep rebuilding the same dropdown."

With apps this big, two new wounds: you couldn't refactor safely, and you were rebuilding the same widgets forever. The fixes here are, honestly, the parts most worth adopting. TypeScript bolts a type system onto JavaScript, catching whole classes of bugs as you type and giving your editor real autocomplete. It's the single biggest shift since you left, and it won so thoroughly that the toolchain is now porting its own compiler to Go to keep up.

For styling, the dinosaur's homeland, the big mover is Tailwind: utility classes right in the markup (class="px-4 py-2 rounded"), which looks like the inline styles you were taught to avoid and provokes exactly the reaction you'd expect, until it quietly becomes the most productive way many people ship UI. Meanwhile native CSS finally grew up, nesting, variables, container queries, :has(), so a lot of the old tooling simply isn't needed anymore.

And components stopped being a dependency you install and became code you own: shadcn/ui (you copy the source into your repo, built on the accessible Radix primitives) reset the whole category. Pair it with TanStack Query for server data, a little Zustand for the rest, Zod to validate it, and Vitest + Playwright to test it, and you've seen the working vocabulary of a 2026 app.

Specimen 02 — the button, fully excavated · 2026 layers: 47
// typed, hydrated, design-tokened,
// copied-not-installed, server-bounded.

<Button
  variant="default"
  size="sm"
  onClick={addToCart}
>
  Buy
</Button>

// + 47 files of build, types,
// CSS engine, a11y, and a runtime.
Accessible by default. Theming via design tokens. Type-checked end to end. It is, in fairness, a better button. It also took an entire ecosystem to render the word "Buy."
source: ~90 bytesbehind it: ~250k files in node_modulesbuild steps: yes
The engineering bolt-ons — the genuinely-worth-it layer
LAYER VII — 39 M 2015 – 2026

Deploying Like It's Easy Again

"Okay. How do I just put it on the internet?"

One layer of pure good news. Remember dragging files into FileZilla and praying? Gone. Now you connect a Git repository to Vercel, Netlify, or Cloudflare, and every git push builds and deploys your site automatically. Open a pull request and you get a live preview URL of that exact change. This part of the future is unambiguously better than 2008.

Two ideas to file away. Serverless functions let you run backend code without operating a server. The edge means that code runs in hundreds of datacenters near your users instead of one, the whole planet as your origin. And if you want your web skills on phones and desktops: React Native/Expo for native apps, Tauri or Electron for desktop (Electron ships an entire browser to show you a chat window; Tauri uses the one you already have).

Hosting & reach — git push is the new FTP, and it's better
LAYER VIII — 44 M 2023 – 2026

The Robot Writes It Now

"Honestly, can the machine just write the React for me?"

The deepest, newest layer, and the one that brought a lot of us back to even look. You can now describe a UI in plain English to v0, Lovable, or Bolt and get a working frontend back, and editors like Cursor, Claude Code, and Copilot write the boilerplate as you type. The practice even has a name, vibe coding, coined by Andrej Karpathy, with the honest asterisk that you still have to understand what came out.

This is also why the lines between engineering branches are blurring. A backend or systems person can now generate a credible frontend in an afternoon, which is wonderful and slightly disorienting, because the generated code quietly assumes you know everything in the eight layers above. That gap, between what the robot writes and what you understand, is exactly what this map is for.

The AI codegen wave — the doorway through which a lot of us wandered back in
Bedrock — 44 m · 2026

You dug forty-four meters
and hit your own front porch.

Here's what's at the bottom, and it's the reason it was worth the trip. The frontier of 2026, the stuff the sharpest people are most excited about, is render the HTML on the server, ship almost no JavaScript, and use the web platform instead of fighting it. Astro, islands, server components, htmx, "the pendulum is swinging back," all of it points one way: toward a fast page made mostly of HTML, served from a CDN.

Which is to say, after two decades and forty-four meters of build tools and bundlers and hydration schemes, the industry sprinted in a giant circle and arrived, slightly out of breath, back at something that looks an awful lot like the file you uploaded over FTP. The instinct didn't expire. The ground moved, for real reasons, and then it moved back.

The Corner Bookshop

Welcome. Browse the shelves or a book now.

Served as HTML. ~3 KB. No build. Loaded before you finished reading this caption.

You don't have to relearn the whole cathedral. You have to learn which 20% is load-bearing, and recognize the other 80% as a buffet you can walk past. The next section is your field kit for exactly that. Welcome back. You weren't late, you were early.

The field kit

Not a prescription, a starting buffet. These are popular, well-supported defaults, not the only right answers. Tap any tool to open its site.

Content site / blog / marketing

Stay close to your instincts

Or go even lighter: htmx with a backend you already know, or hand-written HTML on a CDN. Still completely valid in 2026.

An actual app (logged-in product)

The well-trodden path

It's what the AI tools reach for by default, which mostly means lots of tutorials, jobs, and answers when you're stuck.

The toolbelt underneath, either way

Pick once, forget about it

Deploy by git push. You will likely never open a webpack config in your life. Good.

The acronym / concept decoder

SPASingle Page ApplicationOne HTML shell; JavaScript builds and swaps everything; no full reloads.
SSRServer-Side RenderingThe server builds the HTML per request, then the browser hydrates it.
SSGStatic Site GenerationPre-build all the HTML at deploy time and serve static files. Your old site, with tooling.
RSCReact Server ComponentsComponents that run only on the server and ship zero JavaScript to the browser.
HydrationRe-running JavaScript in the browser to make server-rendered HTML interactive. The expensive part.
IslandsA mostly-static page with small interactive "islands"; ship JS only for those.
HMRHot Module ReplacementSave a file, see the change instantly without a full reload.
ESM / CJSES Modules / CommonJSThe modern import standard vs Node's old require.
JSXHTML-looking syntax written inside JavaScript. Needs transpiling. Not actual HTML.
CWVCore Web VitalsGoogle's speed metrics (paint, responsiveness, layout jank) that made performance a ranking factor.
The edgeYour code running in hundreds of CDN locations near users, instead of one datacenter.