Lesson 29 of 30
Same problems, genuinely different answers: templates vs JSX, fine-grained signals vs component-tree re-render-and-diff, batteries-included vs bring-your-own. Neither is just the other one with different syntax.
18 min read
Every option explains itself — why the right one is right, and why each wrong one is wrong. Miss one and it comes back later on a spaced-repetition schedule.
This topic is about judgment and practical tradeoffs more than a single right answer — a mock interview is the better way to test yourself on it.
Be interviewed on this →Hints without spoilers, and a plain explanation of why a wrong answer was wrong — answered only from this lesson. Free account, no card.
Sign up freeWhat interviewers actually ask about this, and what a strong answer has to cover.
A strong answer covers
Likely follow-ups
If you already know React, most of Angular looks like unfamiliar syntax wrapped around familiar ideas — and for components and props, that's roughly true. But two things are genuinely different, not just relabeled: how the template is written and compiled, and how a state change actually reaches the DOM. Getting those two clear is what makes the rest of Angular click instead of feeling like React with more ceremony.
Templates vs. JSX: JSX is JavaScript — it compiles to createElement()/jsx() calls, so anything you can do in JS you can do inside JSX. An Angular template is a separate, restricted template language with its own syntax ({{ }}, [ ], ( ), @if/@for) that Angular's own compiler parses and understands — you can't drop arbitrary JavaScript expressions into it, but that restriction is exactly what lets the compiler statically analyze the template and generate highly optimized update code ahead of time.
DI, routing, and forms: React deliberately ships none of these — you bring your own router (React Router), your own DI-equivalent (Context, or nothing), your own forms library (React Hook Form, Formik, or raw controlled inputs). Angular ships an opinionated router, a full dependency-injection system, and (increasingly two) forms systems, all built-in and expected to be the default choice. Neither approach is "better" outright — React trades a built-in answer for flexibility and a smaller framework core; Angular trades some of that flexibility for one blessed, consistent answer to questions every real app eventually has to answer anyway.
// Angular — signal-based
import { Component, signal } from '@angular/core';
@Component({
selector: 'app-counter',
template: `<button (click)="count.set(count() + 1)">{{ count() }}</button>`,
})
export class CounterComponent {
count = signal(0);
}
// React — useState-based
function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}
Both render a button that counts up. But what happens underneath a click diverges completely. In the Angular version, count.set(...) updates the signal, and Angular's reactivity graph already knows — from compiling the template — exactly which DOM text node reads count(). It patches that one text node directly. The CounterComponent class instance is never "re-run"; there's no function body to re-execute, because a component here is a class whose methods you call, not a function whose whole body re-executes on every state change.
In the React version, setCount(...) schedules a re-render, which means React calls the Counter function again, top to bottom, producing a brand-new description of what the UI should look like. React then diffs that new description against the previous one (reconciliation) to figure out the minimal real DOM change needed — which in this trivial example is also just one text node, but the path to get there is different: Angular skipped re-running anything outside the signal's known dependents; React always re-runs the component function and relies on diffing afterward to discover that nothing else actually changed.
Both sides start identical: a component reads one piece of state and renders it — count: 0.
This lesson is the direct counterpart to this platform's React lesson on reconciliation and the fiber-based rendering model — both are ultimately answering "how does a state change become a minimal DOM update," and comparing the two side by side is what makes each one legible on its own. It also connects back to the very first idea this platform ever taught: a closure is a function that keeps a live reference to a variable in its enclosing scope — React's function-component model is a closure over props and state on every render, which is exactly why stale closures are a real React bug category. A signal sidesteps that specific failure mode entirely, because there's no per-render closure to go stale in the first place — the signal itself is the single, mutable, always-current source of truth a component reads from.