Angular and React: two opposite answers to the same problem

Almost every comparison between Angular and React still describes two tools that no longer exist. Angular 21 shipped in November 2025 and changed its central mechanism. React stabilised its compiler towards the end of that same year. Both solved the same problem, and it is striking that the solutions are inverse to each other.
It is worth understanding what they fixed, because a much better criterion for choosing comes out of it than any feature list.
The problem they shared
When something changes in an application — the user types, a server response arrives — the corresponding part of the screen has to be updated. The hard question is how the tool knows which part changed.
For nearly a decade, neither of them knew precisely. Both did more than necessary, for different reasons.
How Angular solved it: check everything
Angular used a library called zone.js, which intercepted every asynchronous operation in the browser: timers, events, network requests. Every time one happened, Angular walked the entire component tree comparing values to see which had changed.
It worked, and it had a real advantage: the programmer declared nothing. You assigned a value to a property and the screen updated.
The cost was twofold. On large applications, checking hundreds of components on every click is noticeable. And when something updated too much — or did not update — understanding why was hard, because the cause lived in a library that modified the browser’s behaviour from outside.
How React solved it: run everything again
React took the opposite route. When a component’s state changes, React runs that component function again and all of its children, produces a new description of the interface, compares it with the previous one and applies only the differences to the document.
The model is easier to reason about: given a state, the interface is a function of that state. But re-running the whole subtree on every change is also extra work, and the fix was left to the programmer:
// Marking by hand what should not be recomputed
const sorted = useMemo(() => sort(items), [items]);
const onClick = useCallback(() => select(id), [id]);
export default memo(Row);
That code expresses no product decision: it is scaffolding to avoid unnecessary work. And it is easy to get wrong. An incomplete dependency list produces stale data on screen; an over-full one cancels the benefit. It is a known source of bugs that are hard to find.
The two solutions
This is where each one picked the opposite direction.
Angular made reactivity explicit
Angular 21 moved to signals and turned zone.js off: zoneless mode is the default on new projects.
A signal is a value that knows who is reading it. When it changes, it notifies exactly those places. There is no tree to walk any more: the dependency is registered.
quantity = signal(0);
price = signal(100);
// Recomputed only when one of the two changes, and it notifies whoever reads it
total = computed(() => this.quantity() * this.price());
The parentheses when reading the value are not a syntax detail: they are what makes the read registrable. The dependency ends up written in the code.
Angular 21 also brings Signal Forms, which takes the same model to forms, although it is still experimental. It also replaces Karma with Vitest for testing. The rest of the release is already stable.
React left it alone and added a compiler
React did not change its model. What it did was remove the manual work: the React Compiler reached version 1.0 and has been stable since late 2025.
It analyses components at compile time, works out what can be reused between runs, and inserts that optimisation automatically. The code you write goes back to being the simple version:
// No useMemo, no useCallback, no memo. The compiler handles it.
function Table({ items, id }) {
const sorted = sort(items);
const onClick = () => select(id);
return <Rows data={sorted} onClick={onClick} />;
}
The same goal, with a difference that matters
Both arrived at the application only recomputing what changed. But they answered differently the question of who is responsible for that:
| Angular 21 | React 19 + compiler | |
|---|---|---|
| Where the optimisation lives | In the code you write | In the compile step |
| What you see when reading | The dependencies, explicit | Code with no scaffolding |
| What you have to learn | A new reactivity model | Nothing new; you remove code |
| What it depends on to work | Declaring it correctly | The compiler understanding your code |
That last point is the least discussed. React’s compiler needs components to follow certain rules — do not mutate values you were given, do not produce effects during render — in order to reason about them. When the code follows them, it works without you noticing. When it does not, the compiler declines to optimise that part, and the reason is not visible in the file.
Angular has the inverse problem: nothing is implicit, but you have to write it. And migrating an existing application to signals is real work, not a version bump.
What still separates them
With the mechanism settled, the underlying differences are the same as they were years ago, and they are the ones that should decide the choice.
Angular includes everything. Router, forms, HTTP client, dependency
injection, testing tools: it comes in the box, maintained by the same team and
with one way of doing each thing. On top of that, ng update migrates code
automatically across major versions, including breaking changes.
That matters more than it looks when several teams touch the same application for years, or when staff turnover is high: whoever joins finds a familiar structure rather than the previous team’s particular decisions.
React is a library. It handles the interface and nothing else. The router, data handling, forms and testing are your choice. That lets you assemble exactly what your project needs, and it forces you to decide and to maintain those decisions.
In exchange, the ecosystem is enormous: for any problem there are several mature solutions, and finding experienced people is easier.
On Server Components, announced as the future of React for two years now, it is worth looking at the numbers: they are in 45% of new projects, 6% of developers name them as a difficulty, and single-page applications are still 84.5% of the total. They are stable and useful, but they have not replaced anything yet.
How I would choose
Angular, if the application will live for many years with several teams around it, if you value structural decisions coming pre-made, or if the team comes from typed, object-oriented languages — C#, Java — where dependency injection and classes are familiar ground.
React, if you want control over every piece, if the project has requirements that do not fit a fixed structure, or if you need to hire quickly.
And a warning about the most commonly used criterion: choosing on performance no longer makes sense. After these two changes, both update only what changed. In any real application, the difference will be made by the size of what you download, how many queries the screen runs and how the images are fetched — not by which of the two you picked.
What this change says
There is something here that goes beyond these two tools. For years, writing interfaces included a portion of work dedicated to stopping the tool from doing too much: marking what not to recompute, avoiding unnecessary traversals.
Both, by opposite routes, arrived at that no longer being the programmer’s responsibility. One solved it by asking you to declare the dependencies; the other by working them out on its own. In both cases, the result is that the work disappears from the code and leaves room for what the application actually does.
Comments
Sign in to comment and to like this article.
No comments yet. Be the first to write one.