From jQuery to React Server Components: Making Sense of 15 Years of JavaScript Chaos
Warning: this only exists because I have to come back to this and remind myself of what the fuck is going on about once a year, to understand the tradeoffs between all this crap.
Welcome, fellow backend traveler, to the ever-shifting landscape of front-end development. If you're like me, you dip your toes into this world every so often, only to find that the entire continent has rearranged itself. This post is my attempt to create a stable map, a reference point for the next time I'm forced to leave the comforting predictability of my server-side world.
The Age of jQuery: The Benevolent Dictator (2006)
Before frameworks, there was chaos. We manipulated the Document Object Model (DOM) directly with vanilla JavaScript, a painful process made worse by the browser wars. Each browser had its own quirks and inconsistencies (IE6, I'm looking at you). Writing a simple animation or an AJAX call could mean writing three different versions of the code.
The Design Change: John Resig created jQuery to abstract away these browser differences. It provided a simple, elegant API for DOM manipulation, event handling, and AJAX requests, all wrapped in its iconic
$ function.Why: To make front-end development easier, more consistent, and less painful. The motto was "write less, do more." Instead of dozens of lines of verbose DOM API calls, you could achieve the same result with a single, chained jQuery command.
The Benefit: A single, unified way to write JavaScript that worked across all major browsers. It was a massive productivity boost and became the de facto standard for years. It taught a generation of developers that the web could be interactive without being a nightmare to build.
The First Wave of MVC: The Rise of Structure (2010-2011)
As web applications grew more complex, jQuery's "spaghetti code" became a problem. Large applications became a tangled mess of selectors and callback functions, making them impossible to maintain. We needed structure. The Model-View-Controller (MVC) pattern, popular in backend frameworks like Ruby on Rails, made its way to the front end.
Backbone.js (2010)
The Design Change: Backbone was one of the first to bring MVC to the client side. It was minimalist and unopinionated, providing just the bare essentials: Models for your data, Views to render them, Collections to group models, and a Router to handle URL changes in a single-page application (SPA).
Why: To provide a lightweight structure for building SPAs without imposing a rigid framework. It was a direct reaction to jQuery spaghetti, forcing developers to separate their concerns.
The Benefit: It forced developers to think about organizing their code, separating data (Models) from the UI (Views). Its small size and flexibility made it a popular choice for early SPAs like Trello. It introduced many to the concept of a client-side application that felt like a "real" application.
Knockout.js (2010)
The Design Change: Knockout pioneered declarative bindings and automatic UI refresh through its observable pattern. It introduced the MVVM (Model-View-ViewModel) pattern to JavaScript, with powerful two-way data binding that was more focused and performant than later solutions. You would declare in your HTML how your data should be displayed, and Knockout would handle the updates automatically.
Why: To create rich, responsive displays with a clean underlying data model. Steve Sanderson, its creator, wanted to bring the elegance of WPF/Silverlight's data binding to the web.
The Benefit: Incredibly simple for building dynamic UIs. You could create complex form interactions with minimal code. Its computed observables and dependency tracking were revolutionary at the time. Many concepts that became mainstream in later frameworks were first popularized by Knockout.
Ember.js (2011)
The Design Change: Ember took the opposite approach to Backbone. It was a "batteries-included" framework that valued convention over configuration. It provided a complete solution, with a strong set of opinions on how to build an application, from the data layer (Ember Data) to the router and component model.
Why: To provide a stable, productive environment for building large, ambitious web applications. The Ember team believed that by making decisions for the developer, they could increase productivity and reduce boilerplate. "Stop bikeshedding and build your app."
The Benefit: A clear, consistent structure that made it easy for new developers to join a project and get up to speed quickly. It also introduced a powerful routing system and a strong data layer. Its commitment to stability and backward compatibility made it a favorite in the enterprise world.
AngularJS (2010)
The Design Change: AngularJS, backed by Google, was a beast. It introduced powerful features like two-way data binding, dependency injection, and directives (custom HTML attributes that added functionality). Two-way data binding was the killer feature: changes in the UI would automatically update the model, and changes in the model would automatically update the UI.
Why: To create a more declarative way of building UIs. Instead of manually updating the DOM, you would declare the relationship between your data and your UI, and the framework would handle the rest. It aimed to be a "superheroic JavaScript MVW framework" (Model-View-Whatever).
The Benefit: Rapid development for complex forms and data-driven applications. It felt like magic at the time. However, this magic came at a cost. The "digest cycle" that checked for changes could become a major performance bottleneck in large applications, leading to the infamous "Angular 1.x is slow" reputation.
The Component Revolution: A New Way of Thinking (2013-2014)
The first wave of MVC frameworks was a huge step forward, but they had their own problems. Two-way data binding could become a performance nightmare, and managing state was still a challenge. A new idea emerged: building UIs out of small, reusable, self-contained components.
React (2013)
The Design Change: React, created by Facebook, introduced several radical new ideas:
- •The Virtual DOM: Instead of updating the real DOM directly, React would create a lightweight, in-memory representation of the DOM. It would then "diff" the virtual DOM with the real DOM and only make the necessary changes. This was a huge performance win.
- •One-Way Data Flow: Data flows down from parent components to child components via "props". This made applications easier to reason about and debug, a direct response to the chaos of two-way data binding.
- •Components: Everything is a component. This forced developers to think in terms of reusable, composable pieces, each with its own well-defined responsibility.
Why: To create a more performant and predictable way of building large, complex UIs. Facebook needed a way to manage the complexity of its own application (the Facebook news feed), and React was the solution.
The Benefit: A massive performance boost, a simpler mental model, and a thriving ecosystem of reusable components. React's success was so profound that it shifted the entire industry's focus to component-based architecture.
Vue.js (2014)
The Design Change: Vue, created by ex-Googler Evan You, took the best ideas from Angular and React and combined them into a single, elegant package. It offered the approachability and declarative nature of AngularJS with the component-based architecture and performance of React. It used a virtual DOM but also offered a more familiar template-based syntax.
Why: To create a more progressive and approachable framework. You could use Vue to sprinkle a bit of interactivity onto a page with a simple script tag, or you could use it to build a full-blown SPA with a build system. It was designed to be incrementally adoptable.
The Benefit: A gentle learning curve, excellent documentation, and a flexible architecture that could scale from small projects to large applications. It hit a sweet spot between power and simplicity that resonated with many developers.
The State Management Saga: Where Does the Data Live?
As applications grew, a new problem emerged: state management. When multiple components need to share and update the same piece of data, where does that data live? Passing props down through many layers of components ("prop drilling") became cumbersome and error-prone. This led to a whole new category of libraries.
Flux (2014)
The Design Change: Flux, also from Facebook, was more of a pattern than a library. It complemented React's one-way data flow with a strict, unidirectional data flow for your entire application: Action -> Dispatcher -> Store -> View.
Why: To make state changes more predictable and easier to debug. When a bug occurred, you could trace the flow of data and see exactly what caused the state to change.
The Benefit: A clear and explicit way to manage application state, which was a huge improvement over the chaos of two-way data binding.
Redux (2015)
The Design Change: Redux, created by Dan Abramov and Andrew Clark, took the core ideas of Flux and combined them with concepts from functional programming. It had a single, immutable state tree (the "store"), and all state changes were handled by pure functions called "reducers."
Why: To create a predictable, testable, and debuggable state management solution. The goal was to make state changes predictable.
The Benefit: Time-travel debugging (the ability to step back and forth through state changes), hot reloading, and a rich ecosystem of middleware. It became the de facto standard for state management in React applications for many years, but was often criticized for its verbosity and boilerplate.
MobX (2015)
The Design Change: MobX took a different approach. It used observable data structures and decorators to automatically track changes and update the UI. It felt more like magic, much like the early days of AngularJS, but in a more controlled way.
Why: To provide a more "magical" and less boilerplate-heavy state management solution. It embraced the idea that state management should be simple and automatic.
The Benefit: A simpler API and less code to write. It was a popular choice for those who found Redux too verbose and restrictive.
MobX-State-Tree (2017)
The Design Change: MobX-State-Tree (MST) was built on top of MobX to provide more structure. It brought concepts like models, types, and actions, creating a more opinionated and robust way to manage state, akin to having a database in your frontend.
Why: To combine the simplicity of MobX with the predictability and structure of Redux. It aimed to provide a "golden path" for state management in complex applications.
The Benefit: A powerful and flexible state management solution with features like snapshots, patches, and middleware, while still being easier to work with than Redux for many developers.
The Modern State: Hooks, Context, and Stores
Today, the state management landscape has evolved again.
- •React Hooks and Context API: React's built-in
useState,useReducer, anduseContexthooks have made it possible to manage complex state without an external library for many use cases. - •Zustand, Jotai, etc.: A new wave of simpler, more lightweight state management libraries has emerged, often leveraging the Context API under the hood but with better performance and ergonomics.
- •Pinia: The new official state management library for Vue, offering a simpler and more intuitive API than its predecessor, Vuex.
Convex (2021)
The Design Change: Convex is a radical departure from traditional state management. It's not just a state management library; it's a complete backend platform that handles your database, server functions, and real-time synchronization. Your React components can directly subscribe to database queries, and any changes are automatically synced to all connected clients.
Why: To eliminate the entire category of state synchronization problems. Instead of managing client state and server state separately, Convex provides a unified model where your database is the single source of truth.
The Benefit: Real-time updates out of the box, no need to write API endpoints, automatic optimistic updates, and end-to-end type safety from database to UI. It's particularly powerful for collaborative applications where multiple users need to see the same data in real-time.
TanStack (2019-Present)
The Design Change: TanStack isn't a state management library per se, but a collection of type-safe, framework-agnostic tools that solve specific state management problems. The most popular is TanStack Query (formerly React Query), which manages server state—data that comes from APIs. It handles caching, synchronization, background refetching, and optimistic updates automatically.
Why: To solve the specific problem of server state management, which is fundamentally different from client state. Server state is asynchronous, needs to be cached, can become stale, and should be synchronized across the app.
The Benefit: Dramatically simplifies data fetching logic. No more
useEffect hooks with complex loading and error states. TanStack Query handles all the edge cases for you. The TanStack ecosystem has expanded to include TanStack Router (type-safe routing), TanStack Table (headless table utilities), and more.The Modern Era: The Compiler is the Framework (2016-Present)
The latest wave of frameworks has taken a different approach. Instead of shipping a large runtime library to the browser, they do as much work as possible at build time.
Svelte (2016)
The Design Change: Svelte is not a runtime framework; it's a compiler. It takes your component code and turns it into highly optimized, vanilla JavaScript. There is no virtual DOM. When a variable changes, Svelte generates the precise DOM manipulation code to update the UI.
Why: To eliminate the overhead of a virtual DOM and a runtime library. The Svelte team believed that the best framework is no framework at all (at runtime).
The Benefit: Blazingly fast applications with a tiny bundle size. It also offers a simple, elegant syntax that is a joy to work with. It feels like writing plain HTML, CSS, and JavaScript.
SolidJS (2018)
The Design Change: SolidJS takes the fine-grained reactivity of early frameworks like Knockout and combines it with a modern, React-like syntax (JSX). It doesn't use a virtual DOM; instead, it creates a reactive graph that updates the DOM directly when data changes. Components only run once; they are not functions that are re-executed on every render.
Why: To achieve the performance of Svelte with a more familiar, React-like developer experience.
The Benefit: Some of the best performance benchmarks in the game, with a developer experience that is very comfortable for React developers. It proves that you can have top-tier performance without sacrificing modern ergonomics.
The Rise of Meta-Frameworks: Bringing the Backend to the Frontend
As SPAs became the norm, we lost some of the benefits of traditional server-rendered applications, like fast initial page loads and good SEO. Meta-frameworks emerged to solve this problem by reintroducing the server into the development equation.
Next.js (2016)
The Design Change: Built on top of React, Next.js brought server-side rendering (SSR), static site generation (SSG), and file-based routing to the React ecosystem. It has since evolved to include API routes, middleware, and React Server Components.
Why: To make it easy to build fast, SEO-friendly React applications that can scale from a simple blog to a complex e-commerce site.
The Benefit: A fantastic developer experience with a rich set of features out of the box. It has become the go-to choice for building production-ready React applications.
Nuxt.js (2016)
The Design Change: What Next.js is to React, Nuxt.js is to Vue. It provides a similar set of features, including SSR, SSG, and file-based routing, but for the Vue ecosystem.
Why: To provide a powerful and flexible meta-framework for building Vue applications.
The Benefit: A great developer experience for Vue developers, with a strong focus on performance and developer productivity.
Remix (2021)
The Design Change: Created by the React Router team, Remix takes a fundamentally different approach than Next.js. It focuses on web standards, progressive enhancement, and nested routing. It embraces the platform, using native browser features like forms, cookies, and HTTP caching rather than reinventing them in JavaScript.
Why: To build better websites by working with the web platform rather than against it. The Remix team believed that many modern web apps had become unnecessarily complex by ignoring web fundamentals.
The Benefit: Exceptional performance through smart data loading, automatic error boundaries, and progressive enhancement. Forms work without JavaScript, data mutations are simple, and the developer experience feels more like traditional web development—in a good way. It was acquired by Shopify in 2022, signaling its importance in the ecosystem.
SvelteKit (2020)
The Design Change: SvelteKit is the official meta-framework for Svelte. It provides a modern, flexible, and powerful way to build Svelte applications, with features like server-side rendering, routing, and serverless functions.
Why: To provide a first-class, full-stack development experience for Svelte.
The Benefit: A seamless development experience that combines the power of Svelte with the features of a modern meta-framework.
The Return to Simplicity: HTML-First Frameworks (2020-Present)
Not every framework is racing toward more JavaScript. Some developers are pushing back against the complexity and taking a different approach entirely.
htmx (2020)
The Design Change: htmx isn't really a JavaScript framework at all. It extends HTML with attributes that allow you to make AJAX requests, handle events, and update the DOM without writing JavaScript. You write attributes like
hx-get="/api/users" and hx-swap="innerHTML" directly in your HTML.Why: To radically simplify web development by returning to the original hypermedia model of the web. The htmx team believes that most applications don't need the complexity of a full JavaScript framework.
The Benefit: Dead simple to learn and use. No build step, no bundler, no virtual DOM, no state management. Just HTML with superpowers. It's particularly loved by backend developers who want to add interactivity without diving deep into the JavaScript ecosystem.
The Edge and Beyond: The Future is Now (2021-Present)
The very latest frameworks are pushing the boundaries even further, focusing on new architectural patterns and deployment models. And to answer the question you're probably thinking: yes, people are actually using these in production. While they're not as widespread as the big three, companies like Google, Vercel, and Cloudflare are investing in and using these new technologies.
Astro (2021)
The Design Change: Astro introduced the concept of "islands architecture." Most of your site is static HTML, with small, interactive "islands" of JavaScript where needed. But here's the kicker: these islands can be written in React, Vue, Svelte, or any other framework. You can even mix frameworks on the same page. Astro also pioneered "partial hydration"—components only become interactive when they need to be, based on viewport visibility or user interaction.
Why: To build content-rich websites that are fast by default. Astro's philosophy is to ship as little JavaScript as possible. The team recognized that most of a typical website is static content that doesn't need JavaScript at all.
The Benefit: Incredible performance for content-focused sites like blogs, marketing sites, and e-commerce stores. Perfect Core Web Vitals scores out of the box. The ability to use any framework (or no framework) gives teams flexibility to use the best tool for each component. It's become the go-to choice for documentation sites and content-heavy applications.
Qwik (2021)
The Design Change: Qwik is a "resumable" framework that fundamentally rethinks how applications boot up. Traditional frameworks serialize data on the server, send it to the client, then re-execute all the component logic to "hydrate" the page. Qwik serializes both the data AND the application state, allowing it to "resume" exactly where the server left off. It achieves O(1) time to interactive regardless of application size through fine-grained lazy loading—it only loads the code for the specific event handler you're interacting with.
Why: To achieve instant-on applications, no matter how complex. Created by Miško Hevery (the original creator of Angular), Qwik aims to solve the fundamental problem that all JavaScript frameworks face: the more interactive your app, the more JavaScript you need to download and execute before it becomes interactive.
The Benefit: The potential for incredibly fast-loading applications, even on slow networks and devices. Sub-second time to interactive on complex applications. The framework automatically handles code splitting at a granular level—you don't need to think about lazy loading. It's particularly powerful for e-commerce and applications where first-interaction speed directly impacts revenue.
So, What the Hell Do I Use?
And that's the million-dollar question, isn't it? The honest answer is: it depends on what you're building. Unlike the early days, there's no single "best" framework anymore. Instead, we have a spectrum of specialized tools.
- •Building a highly interactive, app-like experience?
- •Next.js (React) is the industry standard, with a massive ecosystem and a clear path to production.
- •Nuxt.js (Vue) is a fantastic and mature alternative if you prefer Vue's ergonomics.
- •SvelteKit is a joy to use and produces incredibly fast apps, perfect for teams who value developer experience and performance.
- •Building a content-focused website (blog, marketing site, portfolio)?
- •Astro is arguably the best tool for the job. Its islands architecture gives you incredible performance by default.
- •Need to add some dynamic sprinkles to a server-rendered backend application?
- •htmx is a game-changer. You can get 90% of the interactivity of a SPA with 10% of the complexity.
- •Alpine.js (a spiritual successor to jQuery) is another great option for this.
- •Is initial load time the single most important metric for your business (e.g., e-commerce)?
- •Qwik is designed from the ground up to solve this problem and is worth serious consideration.
- •Working in a large enterprise with many teams?
- •Angular's rigid structure and opinionated nature can be a huge asset for maintaining consistency and quality at scale.
As for me? I'm going back to my cozy backend world where the most controversial topic is whether to use tabs or spaces. I've updated my calendar to have my yearly existential crisis about frontend development in 12 months. By then, I expect we'll have abandoned components and will be writing our UIs directly in WebAssembly using a framework compiled from ancient Sumerian clay tablets. See you then.