I built the same app 10 times // Which JS Framework is best?
Based on Fireship's video on YouTube. If you like this content, support the original creators by watching, liking and subscribing to their content.
A framework choice is primarily about how state connects to UI and how lifecycle/events are handled, not about popularity alone.
Briefing
A single “best” JavaScript framework doesn’t exist—choosing one comes down to how each framework handles the same core job: keeping UI and application state in sync. To make that trade-off concrete, the walkthrough rebuilds the same to-do app ten times, starting with vanilla JavaScript and then repeating the task in Angular, React, Vue, Svelte, Lit, Alpine, Solid, Stencil, Mithril, and finally plain HTML/JS again. The exercise matters because the framework decision shapes everything that follows: how events are wired, how data flows, how lifecycle is managed, and how much boilerplate ends up in real projects.
The baseline vanilla implementation shows why frameworks gained traction. The to-do app stores items in localStorage, but vanilla requires imperative DOM work: querySelector to grab elements, createElement to build list items, innerHTML updates to reflect state changes, and event listeners to respond to form submissions. That approach leaves state and UI loosely coupled, making it easy for the interface to drift out of sync as features grow. Even basic maintainability suffers—markup doesn’t reveal which events are attached, so developers must hunt through JavaScript to understand behavior. Lifecycle adds more complexity: on initialization, the app must read localStorage and manually render existing items.
React is presented as a declarative UI “gold standard” built around components and reactive state. A component function returns JSX, state is managed with useState, and the UI re-renders automatically when state changes. Form submission is bound directly in JSX via onSubmit, and input values are read with hooks like useRef. Lifecycle work—loading saved to-dos—moves into useEffect with an initialization dependency array. The trade-off is that React stays minimal and leaves many architectural decisions (routing, animation, state management) to the ecosystem.
Angular flips the philosophy by being highly opinionated and structured. It uses TypeScript, enforces conventions through its project layout, and provides built-in support for routing, animation, and server-side rendering. Templates use Angular’s templating language (including ngFor for loops), and forms rely on directives like ngModel for two-way binding—at the cost of a steeper learning curve and more framework-specific concepts.
Vue is framed as approachable and similar in spirit to Angular, with official packages for routing and state management plus a large third-party ecosystem. Svelte and Solid push performance and simplicity further by compiling away runtime overhead: Svelte turns components into efficient JavaScript, while Solid compiles to native DOM nodes and avoids a virtual DOM. Both emphasize readable, minimal code patterns (Svelte’s bind and lifecycle hooks; Solid’s signals and onMount).
For web components, Lit and Stencil compile to standard custom elements, aiming for cross-framework compatibility. Lit leans on template literal syntax and web-component APIs like connectedCallback, while Stencil offers an Angular-like component model with JSX-like templating. Alpine takes the opposite direction: tiny, HTML-first reactivity for sprinkling interactivity into existing pages, using directives like x-data, x-for, and x-on plus an Alpine store for shared state. Mithril rounds out the set as a lightweight, JavaScript-first approach with a virtual DOM and a UI defined through pure JS functions.
Across all ten builds, the consistent message is pragmatic: frameworks can all implement the same to-do app, but they differ sharply in how they connect state to UI, how much structure they impose, and how they scale developer experience for teams and long-term maintenance.
Cornell Notes
The walkthrough rebuilds the same to-do app ten times to show that “best” JavaScript framework depends on trade-offs, not downloads or popularity alone. Vanilla JavaScript makes state/UI synchronization manual and error-prone, requiring imperative DOM updates, event wiring, and explicit lifecycle handling. React improves maintainability with declarative components, reactive state (useState), and lifecycle (useEffect), while Angular adds strong conventions and built-in tooling using TypeScript and template directives like ngFor and ngModel. Svelte and Solid emphasize compilation and performance by reducing runtime overhead, while Lit and Stencil target web components for cross-framework reuse. Alpine and Mithril demonstrate two different philosophies: HTML-first interactivity and JavaScript-first UI definitions, respectively.
Why does vanilla JavaScript feel harder to scale for a non-trivial app?
What makes React’s to-do implementation more declarative than vanilla?
How does Angular’s approach differ from React’s in structure and data binding?
What’s the key distinction between Svelte/Solid and frameworks that rely on a runtime like React?
What’s the purpose of Lit and Stencil in this comparison?
When would Alpine or Mithril be a better fit than a full SPA framework?
Review Questions
- In vanilla JavaScript, what specific mechanisms must be implemented to keep state and UI synchronized, and why does that become brittle?
- Compare how React and Angular each handle lifecycle and form input binding in the to-do app.
- Which frameworks in the set compile to standard web components, and what problem does that compilation target?
Key Points
- 1
A framework choice is primarily about how state connects to UI and how lifecycle/events are handled, not about popularity alone.
- 2
Vanilla JavaScript requires imperative DOM updates and manual lifecycle logic, which quickly becomes difficult to maintain as features expand.
- 3
React’s declarative component model ties UI rendering to reactive state (useState) and lifecycle (useEffect), reducing manual synchronization work.
- 4
Angular’s conventions and TypeScript-first structure (ngFor, ngModel, ngOnInit) can improve consistency for large teams but increases the learning curve.
- 5
Svelte and Solid reduce runtime overhead via compilation, aiming for simpler code and strong performance, with ecosystem size as a practical trade-off.
- 6
Lit and Stencil target cross-framework reuse by compiling to standard custom elements (web components).
- 7
Alpine and Mithril represent different philosophies: HTML-first sprinkling of interactivity versus JavaScript-first UI definition.