Component-based Frameworks

  • Almost all frontend frameworks are syntax/ergonomic sugar on top of native JS and the browser.

  • None add fundamentally new runtime capabilities that the browser can’t already do.

  • .

  • No JS framework suddenly gives threads, parallel execution, or new DOM APIs.

  • They cannot extend what the browser can do.

  • Why frameworks exist :

    • They reduce repetitive wiring for large apps.

    • They integrate state, DOM updates, and async requests in a structured way.

    • They handle edge cases (race conditions, lifecycle, error states, component reuse) that become messy in raw JS.

    • Automate common patterns

    • Reduce bugs and boilerplate

    • Provide a declarative programming model

Comparisons
  • Reactivity :

    • React/Preact use component-level state and rerender subtree. Solid uses signals; updates are localized. Qwik uses signals/refs but focuses on serializing and lazy event handlers.

  • Rendering :

    • React/Preact VDOM diff. Solid direct DOM ops (compiled). Qwik renders on server then resumes only what’s needed.

  • Hydration :

    • React/Preact full or partial hydration. Solid partial hydration techniques. Qwik aims for no traditional hydration — it resumes.

  • Bundle/runtime :

    • Preact < React (smaller). Solid small runtime for reactivity. Qwik pushes logic to server/serialized payload; runtime focuses on resuming.

  • Use case :

    • React/Preact good for heavy interactivity and library ecosystem. Solid for maximum update performance. Qwik for extreme lazy-load and fast TTFP.

Vanilla JS (Not a framework, I'm just comparing)

Characteristics
  • AJAX:

    • fetch()  or XMLHttpRequest  (older API).

  • Problems appear as you scale:

    • Multiple endpoints → repeated fetch calls.

    • Multiple components updating DOM → repeated innerHTML  updates.

    • Error handling → repeated try/catch blocks.

    • State management → you must track which component has which data.

    • UI updates tied to async → manually coordinate loading states, spinners, etc.

Examples
  • More complex async behavior :

    const widgets = [
      { id: "widget1", url: "/data1" },
      { id: "widget2", url: "/data2" },
      { id: "widget3", url: "/data3" },
    ];
    
    const status = document.getElementById("status");
    
    let completed = 0;
    
    widgets.forEach(w => {
      const el = document.getElementById(w.id);
      el.innerHTML = "Loading...";
    
      fetch(w.url)
        .then(res => {
          if (!res.ok) throw new Error("Network error");
          return res.text();
        })
        .then(html => {
          el.innerHTML = html;
        })
        .catch(err => {
          el.innerHTML = "Error loading widget";
          console.error(err);
        })
        .finally(() => {
          completed++;
          if (completed === widgets.length) {
            status.innerHTML = "Dashboard loaded";
          }
        });
    });
    

Svelte

  • No Virtual DOM.

  • Works as a compiler, to turn the .svelte  code into plain JS.

  • Full TypeScript support ( .svelte  files with <script lang="ts"> )

  • Compile-time type checking on props, stores, and events

  • 1 component per file.

  • Every .svelte  file has 3 parts:

    • script + markup + style.

  • Very fast runtime, small bundle

  • AJAX:

    • Fetch or load  functions use AJAX under the hood.

Examples
  • Button increases a counter :

<script>
  let count = 0;
  const increment = () => count++;
</script>

<p>Count: {count}</p>
<button on:click={increment}>Increment</button>
  • Ex2 :

<script>
  export let items = [];
</script>

{#if items.length}
  <ul>
    {#each items as item}
      <li>{item.title}</li>
    {/each}
  </ul>
{:else}
  <div>No items</div>
{/if}

Qwik

  • .

Solid

  • No Virtual DOM.

  • Similar to React.

  • Uses JSX.

  • Kinda like React, but with a Svelte-like compiler

  • "A more well thought-out and faster version of React".

Examples
  • Button increases a counter :

import { createSignal } from 'https://cdn.skypack.dev/solid-js';
import { render } from 'https://cdn.skypack.dev/solid-js/web';

function Counter() {
  const [count, setCount] = createSignal(0);
  return (
    <div>
      <p>Count: {count()}</p>
      <button onClick={() => setCount(count() + 1)}>Increment</button>
    </div>
  );
}

render(() => <Counter >, document.getElementById('app'));
  • Ex2 :

import { For, Show } from "solid-js";

export default function List(props) {
  return (
    <Show when={props.items.length} fallback={<div>No items</div>}>
      <ul>
        <For each={props.items}>{item => <li>{item.title}</li>}</For>
      </ul>
    </Show>
  );
}
import { For } from "solid-js";

export default function Users(props) {
  return <ul><For each={props.users}>{u => <li>{u.name}</li>}</For></ul>;
}

Vue

  • React's components, Angular's templates.

  • A .vue  file is:

    • Template + Script + Style

Characteristics
  • Has Virtual DOM.

  • AJAX:

    • Vue itself doesn’t do AJAX, but most Vue apps use it for components.

Examples
  • Button increases a counter :

<div id="app">
  <p>Count: {{ count }}</p>
  <button @click="increment">Increment</button>
</div>

<script type="module">
import { createApp, ref } from 'https://unpkg.com/vue@3/dist/vue.esm-browser.js';

const App = {
  setup() {
    const count = ref(0);
    const increment = () => count.value++;
    return { count, increment };
  }
};

createApp(App).mount('#app');
</script>
  • Ex2 :

<template>
  <div>
    <ul v-if="items.length">
      <li v-for="item in items" :key="item.id">{{ item.title }}</li>
    </ul>
    <div v-else>No items</div>
  </div>
</template>

<script setup>
defineProps({ items: Array });
</script>

Preact

  • React-compatible API, smaller runtime.

  • Has Virtual DOM.

  • Uses JSX.

Examples
  • Button increases a counter :

import { h, render } from 'https://unpkg.com/preact@latest?module';
import { useState } from 'https://unpkg.com/preact@latest/hooks/dist/hooks.mjs?module';

function Counter() {
  const [count, setCount] = useState(0);
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}

render(<Counter >, document.getElementById('app'));

React

  • From Facebook, 2013.

  • Uses JSX.

  • Has Virtual DOM.

  • Svelte or Solid sounds much better than this thing.

  • AJAX:

    • Via fetch , Axios, or libraries like React Query; client-side rendering is driven by async data.

Asynchronous Behavior
  • React doesn’t fundamentally change how concurrency works—JS is still single-threaded—but it improves ergonomics and state management around asynchronous operations

  • React doesn’t add new concurrency primitives—it doesn’t give JS threads, parallelism, or async I/O that didn’t exist.

  • Everything React does is built on native JS: Promises, the event loop, fetch , timers, etc.

  • Automatic DOM diffing  â†’ no manual innerHTML  manipulation.

    • Your component re-renders automatically when state changes.

    • In Vanilla JS :

      fetch("/data")
        .then(res => res.text())
        .then(html => document.getElementById("widget").innerHTML = html);
      
      • You must track which element corresponds to which data.

      • Any additional async operations updating the same element require careful coordination.

    • In React :

      const [data, setData] = useState(null);
      
      useEffect(() => {
        fetch("/data")
          .then(res => res.text())
          .then(setData);
      }, []);
      
      return <div>{data || "Loading..."}</div>;
      
      • React automatically re-renders the component when state changes.

      • No manual DOM manipulation.

      • Concurrency conflicts are minimized because state updates are batched and applied in order.

Examples
  • Button increases a counter :

import { useState } from "react";
import { createRoot } from "react-dom/client";

function Counter() {
  const [count, setCount] = useState(0);
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}

const root = createRoot(document.getElementById("app"));
root.render(<Counter >);
  • Simple async behavior :

function Component() {
  const [data, setData] = React.useState(null);

  React.useEffect(() => {
    fetch("/data")
      .then(res => res.json())
      .then(setData);
  }, []);

  return <div>{data}</div>;
}
  • More complex async behavior :

    function Dashboard() {
      const [widgets, setWidgets] = React.useState([]);
      const [loaded, setLoaded] = React.useState(false);
    
      React.useEffect(() => {
        Promise.all([
          fetch("/data1").then(r => r.text()),
          fetch("/data2").then(r => r.text()),
          fetch("/data3").then(r => r.text()),
        ])
          .then(results => setWidgets(results))
          .finally(() => setLoaded(true));
      }, []);
    
      return (
        <div>
          {widgets.map((w, i) => (
            <div key={i}>{w || "Loading..."}</div>
          ))}
          {loaded && <div>Dashboard loaded</div>}
        </div>
      );
    }
    

Angular

  • Google, 2010.

  • Angular 2 in 2016.

  • Requires TS.

Characteristics
  • Has Virtual DOM.

  • Reactivity model: Change detection.

  • AJAX:

    • Has built-in HttpClient  for AJAX requests; components update via async data.

  • Template syntax, but bound tightly to TypeScript logic.

// user-list.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'user-list',
  template: `
    <ul>
      <li *ngFor="let u of users">{{ u.name }}</li>
    </ul>
  `
})
export class UserListComponent {
  users = [{ id:1, name:'Alice' }, { id:2, name:'Bob' }];
}
Examples
  • Button increases a counter :

// counter.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-counter',
  template: `
    <p>Count: {{ count }}</p>
    <button (click)="increment()">Increment</button>
  `
})
export class CounterComponent {
  count = 0;
  increment() { this.count++; }
}

// app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { CounterComponent } from './counter.component';

@NgModule({
  declarations: [CounterComponent],
  imports: [BrowserModule],
  bootstrap: [CounterComponent]
})
export class AppModule {}

Backbone

Ember

  • Convention over configuration.

Lit

  • Google.

  • Focused on Web Components.

Stencil

  • Focused on Web Components.

Mithril

  • Virtual DOM.