React isn't Javascript. It's a franken-language that looks superficially like a mix of JavaScript and XML whilst following the rules of neither. That's why there is such a thing as a React compiler - a good sign that you're not writing JS, which doesn't have compilers.
The other hint should be all the new rules you have to follow. React is full of magic syntax that looks like functions, but e.g. you can't call them inside an if statement, or you have to redundantly declare the 'dependencies' (variables you read) and so on. The semantics of React code are very different to imperative straight line code.
You say that like it's a bad thing - but it didn't stop Babel or TypeScript from being popular, both of which need a compiler. And being honest, I don't like extra tools in my chain, which is probably why I don't use React proper, but that proves you don't need anything React specific for anything other than optimisation
The only syntax you really want for React is JSX. Which is just because writing React.createElement("div" ...) every time is unergomomic. JSX isn't specific to React. It could easily be a language feature, in fact it is an OOTB language feature of TypeScript.
> React is full of magic syntax that looks like functions, but e.g. you can't call them inside an if statement
They look like function calls, because they are. They're not "magic syntax", and in fact, those rules are explicitly because theres no way around that without implementing special syntax
It would be best to think of it as syntax sugar for create Element() function calls. You enter JSX with angle tags and insert expressions in braces
> React is full of magic syntax that looks like functions, but e.g. you can't call them inside an if statement, or you have to redundantly declare the 'dependencies' (variables you read) and so on
That's not magic syntax. That's just functions that must be processed in regular order between render ticks.
It's not a difficult exercise to write a "plain JS" function that works that way. If you've worked much with closures you can visualise how that would play out.
Thats what makes it a new language. C is just sugar on top of assembly.
Its so strange that jsx needs a buildstep, but misses ton of obvious ergonomics that one could have added. Why className instead of class? With a buildstep, it would surely be possible to determine if class was a keyword or not based on context
I think that's a bad example. C isn't a sugar for assembly: For example what assembly code does a function prototype desugar into? Sugars have 1:1 mappings with host code
Maybe you're just being rhetorical about the spectrum of macros, sugars, and languages, though.
(In my opinion, a better target for that criticism, in JS land, is the Jest test framework, because of all the reordering magic it does that break fundamental ES semantics)
> Why className instead of class?
This hasn't been a constraint since... I want to say React 18, six or seven years ago? I might be misremembering the history but I think that was only a problem in the Early Days when there was paranoia about whether JSX needed to be conservative with reserved keywords
The way you know it's magic is it shatters the principle of referential identity, which tells you that a variable is itself. It pretends you can use a simpler mental model but you really cannot and must not.
When you do a useValue() it is clear you are grasping at something with a larger lifespan than your function.
Conceptually it is not much different than
int counter() {
static int x = 1;
return x++;
}
Which predates C89, if age is an argument"React Functional Components" have nothing to do with conventional functional programming. They inherently (intentionally?) violate the spirit of functional programming through hidden global variables, while pretending that it is still functional, when in reality they should be doing the opposite. They should be upfront about the hack that hooks are. They are not functional, they are merely functions. In all other respects they operate as if the function was a method inside a class and that all hook calls are with respect to an invisible "this" keyword that contains the component's context. Since hooks are not named (which would expose their inherently pseudo-OOP nature), they associate their state through a hidden incrementing counter. It's like you're sweeping all the OOP parts under the rug to pretend to be functional so you can be a cool functional hippie too.
This happens because GUIs are inherently imperative constructs.
The thing is ... you can model effects with the Free monad (or with Haskell's Backpack as was on here just the other day - https://news.ycombinator.com/item?id=45221112), so they are definitely "functional", for most variations on the definition of "functional" that I know.
Functional literally means dealing with functions composition.
Free of side effect is not a property of functional programming. Ocaml is functional and has side effects.
Next you're going to complain about the fact that the compiler handles static variables with controlled allocations for you in C.
Like there's no rational basis for your complaints or critique other than grandstanding.
More like syntax cilantro. Some people love it. Some can't stand it.
React "compiler" is in fact a transpiler, which is a very common thing in JS.
>React is full of magic syntax that looks like functions
Full of magic syntax meaning 5 straightforward hooks? Surely they are not true free-form JavaScript, but at least the syntax itself is not turned into a DSL
https://react.dev/learn/react-compiler/introduction#what-doe...
The React cinematic universe has a habit of repurposing existing terminology, but they're both transpilers, to the extent that "transpiler" is even a word.
It appears that I was wrong about the definition of transpilation. It's a specific term for a compiler that compiles from a high-level language to another high-level language, even when those languages are the same with no DSL and even when the logic is optimized.
I stand corrected.
Yes, all transpilers are compilers, but not all compilers are transpilers.
`useEffect` is straightforward? Cloudflare recently (like, literally 4 days ago)[1] had a pretty big outage because of the improper use of `useEffect` (surprise, surprise, it was the dependency array), a hook so infamous if you search "When should I use `useEffect`" you'll get approximately 9 trillion hits, with nearly all of them telling you something different, including Dan Abramov himself having to chip in with multiple blog posts telling people to NOT use it because of how much of a dumpster fire of a API it is.
[1] https://blog.cloudflare.com/deep-dive-into-cloudflares-sept-...
You become reliant on all your ancestors doing the right thing, in every situation, forevermore. One mistake and unstable references cascade everywhere. The surface for introducing errors is huge, esp in a large, complex codebase. And we have no way to guarantee a stable reference.
This is why we never modify data but create new data. Data must be immutable. Strictly typed. Always in the form it is expected to be. Otherwise - crash immediatelly.
>But later someone introduced
This is why we write integration tests. Introducing anything without tests is only guesswork.
It is practically impossible to have full coverage of all code paths in integration tests, since there is a combinatorial explosion of paths. In a case where you have 3 components each with 3 paths you have 27 unique paths. And no app is that simple in practice. It gets out of hand quickly.
Also I just want to point out again that Dan Abramov had to write an insanely long guide to using useEffect [1]. This piece is around 10k words explaining in-depth how useEffect works. That is insane for one of the fundamental, core methods exposed by a library.
B) rakes can't be changed to prevent that, software apis can.
useEffect usage needs to die, yes. I don't think it's a case against React, given its age.
Otherwise, using React is straightforward. I started coding in it without reading any docs. As someone who used Dojo, prototype, ext.js, jQuery (+UI), Backbone.js, knockout (still has a special place), Mithril, the classic angular, vue (2), svelte, Marko and even Solid (I'm not totally sold on signals), React is the most straightforward by a mile.
Is it the most performant? Hell no. Is it the one with the least gotchas/warts? Nope. Concise? A giant no. Batteries included? It depends on what you're looking for...
But I can just be productive with it, and it makes sense.
You saying you can be productive in react is just ironic. I just read it as, I can be employable using React.
edit: also about the moms cooking analogy... With many of those libs/frameworks, I have much more experience with than react.
You could go back 13 years, show somebody some JSX and ask them what language they think it is and nobody would be well that's obviously JavaScript with a bit of sugar on top.
It did not.
And in the decade I've been writing for React, I've never used the React compiler. But saying that it's proof that it's not JavaScript it's like saying that V8 is proof that JavaScript is too complicated because otherwise it would be fast enough not to need a JIT.
And all those rules? You don't have to follow them either. Use class components. They're still there, and they work fine.
I understand how they map to each other but I've only done JSX. That said, I'm old so calling functions all over the place is in my wheelhouse, and I'd love to hear from folks who have done both.
So from my personal experience the things you mentioned are non-issues in practice. Do you have a different experience?
As for hooks, I guess I mentally treat them as magic that lives in its own lane, and so I've never really thought that I could do anything with them, other than what the manual prescribes.
No. Plain JS requires no compilation.
Yes and all you have to do is learn why those semantics exist in order to do react well.
Unfortunately too many programmers still think they can just mimic other code to figure it out.
This is actually the selling point to me. As someone who started learning pure HTML, JSX just makes a lot of sense to me intuitively. It feels like intelligent HTML
and now it's failing for the same exact reason, a pseudo "just js function" dsl (hooks) and all the magics enabled by special "compilers" and toolchain.
(To get ahead of the common objection: of course it's still JavaScript by virtue of being implemented in JavaScript. But so are Svelte, Vue et all)
Hooks are functions that need to be called before early returning, in a function that provides a context for them. They are also just Javascript, they can be implemented with no build tools.
If you haven't, I recommend reading the (original?) article https://journal.stuffwithstuff.com/2015/02/01/what-color-is-...
This is like when you are doing embedded programming, holding the API functions you need to call in a special sequence to the same standard as people writing their own DSLs with templates.
I would not say it's a different definition of colour. I am somewhat contorting the original definition in the article, but if you compare characteristics listed, many/most of them apply to hooks also
Hook definitions can be a composition of reusable functions and other hooks, like any event handlers (e => filterKeys('cmd+s, ctrl+s', preventDefault(e)).then(save)). It's possible to break this anology (you can register an event handler in an if branch) but I hope it gets the point across.
Yes, and those are frequently annoying also. Literally the point of the article is the friction they introduce and questioning whether there are better ways to do things. Sometimes there are.
I always hated templating languages
> Greenspun's {{rules.length | ordinal}} rule:
> Any sufficiently complicated templating language contains an ad-hoc, informally-specified, bug-ridden, slow implementation of half of the host language
But after working with Vue for a 18 months and expecting to hate it, I actually very much enjoyed it. The biggest downside is strictly one component per file.
Edit: in case I've come across as some kind of react hater, I was an early adopter and still use it professionally to this day
Agreed, and Vue does it right exactly because they don't let you do complex things in the template. You can use ternaries in a pinch, but `computed`s exist so there isn't even a reason to do stuff like that. There's like... 10? directives that you can use and they are extremely obvious as to what they're doing, and the handlebar syntax itself is extremely limited and doesn't let you do any crazy stuff with it.
Aside: I wish other frameworks would steal Vue's event modifiers. Doing things like `@click.prevent` or `@keyup.prevent.down` is so nice
I think semantics could be a more objective way to assess the DX of frameworks because you can have/add a syntactic layer on top that suits your preferences [1]. Semantics would be things like rules of hooks, footguns of `useEffect`, component level reactivity rather than fine-grained reactivity, etc. The high level outcome of this would be being able to answer the following question:
"How likely is it that this framework will put me in the pit of success with minimal overhead?"
[1] https://vuejs.org/guide/extras/render-function.html#jsx-tsx
With jsx, I don't think it's possible - I need to return it.
So even if Svelte is a DSL, to me it feels closer to web standard and jsx
And god help you if you want to mix component level code inside React-land and "vanilla". They had to add 3 new hooks over the last 5 years to try and alleviate those issues, all while gaslighting the community and tool authors that the were "holding it wrong".
SolidJS is far better when it comes to how well it follows expectations, and how well it integrates.
I'll likely NEVER use anything that doesn't let me run JSX.
My personal preference is for complexity at the start of the render pipeline (e.g. in state) or at the end (e.g, in JSX).
So I personally dislike complex hooks composition, but I can live with it. (My) teams seem to like it. I'd rather have boilerplate of redux, or redux sagas - or a S.O.L.I.D framework + scaffolding tools, and turn the composition of logic part of my brain off.
But the context switch to maintaining scaffolding tools is perhaps a bit of a jump.
As an aside: I'm shocked to see Yeoman largely diminished in activity, and Hygen (https://github.com/jondot/hygen) not getting anywhere near enough love as it deserves etc.
Perhaps there is some, first-class macro or meta programming or combination of the two that is missing. Or maybe its hard to invest in tools you can't necessarily take from job to job - as scaffolding tools are capturing opinion.
Also calling it "just JavaScript" might've been true in the Class component days, but in this frankenstein "functional" component paradigm we're at today it's far from the truth. I mean, it's not even a JS file, it's a JSX file for starters.
At the end of the day the whole JSX vs HTML-DSL thing comes down to a personal preference, and I doubt it's had much to contribute in terms of the success of the various frameworks. I know plenty of people working with React that despise JSX, and I know plenty of people working happily with Vue or Svelte that hate the DSL for templates.
<> // React code here <>
react.createElement(react.Fragment, /* React code here */)Except it's not. It has a bunch of footguns and implicit behaviours that make no sense unless you're well-versed in the internals of React.
See: https://macwright.com/2024/09/19/the-extra-rules-of-hooks
https://www.schiener.io/2024-03-03/react-closures
And JSX by itself is already a DSL, which drastically changes how it works based on the pragma used. JSX for SolidJS, Preact, Inferno or any other framework has completely different internals.
(Not saying React is bad, just that DSLs impeding adoption rings hollow in light of Tailwind/JSX.)
I agree with JSX, and function components are nice, but React is most definitely not just function composition. Hooks add state and introduce life cycle events that take components way beyond stateless territory, to the point that it would be far easier to reason about them if they were class components with properties and decorators and handlers invocable through inversion of control.
When your just using vanilla react, I've never had a problem with hooks being that hard to reason about. Once you add in SSR, routers, query caching frameworks, etc the "lifecycle & state" starts to get confusing.
This is mostly a problem with the additional complexity of these frameworks (nextjs, tanstack) and not react at its core. Building an app with just react (and maybe redux) feels so simple and natural once you learn the paradigm.
I guess redux solves both "state" and "messages" at once, that's good. But what you work with then is not "vanilla react" at all. I don't even think "vanilla react" can really exist, beyond toy examples. You either use frameworks or write them yourself (worse).
You can develop extremely powerful web-apps using just React + Redux. Once you need to start dealing with SSR for SEO & server side caching, data preloading, hydration, etc... things get complex.
But honestly that's because those concepts are inherently complex and that complexity can only be reduced so far. Another problem is they get baked into these framework as the "default" and often over utilized when they aren't actually needed.
Yes, class components.
Function components help in reducing boilerplate code when all you want to do is put together a component that is a thin wrapper over other components with barely any state or logic.
The problem with function component fundamentalism rears its head when you start to add state and life cycle handlers, as you start to pile hooks on top of hooks when simple inversion of control would make things cleaner and easier to manage.
Literally the main criticism thrown at class components is boilerplate code and support for life cycle methods. It turns out that they are a nuisance when writing small stateless components, but are an unavoidable need when you add non-trivial state and life cycle handlers.
They each have their own quirks. Personally I'd rather write language specific if/else (obvious, can't get it wrong) than have to remember to reactify my functions if I want to avoid subtle performance issues.
I want to increment some counter on the webpage. Which approach feels natural?
increment = () => {
this.setState((prevState) => ({ count: prevState.count + 1 }));
};
const increment = () => setCount((count) => count + 1);
function increment() {
count += 1;
}
No one wakes up saying "please let me mutate simple state with function calls". function increment() {
count += 1;
dispatchEvent(new CustomEvent(
"count-incremented",
{ detail: { count } }
));
}
I think I prefer not having to manage events all over the place.Vue also isn't too far:
function increment() {
count.value += 1
}
In 2025, React state management is complete shitshow compared to Svelte and Vue.How in the world people are claiming "React is just Javascript" is beyond me.
No one writing markup says "Damn, I wish I could write this inline with my javascript." Have you even seen a react component before? People writing javascript containing the markup which, in turn, contains more inline javascript statements for conditional rendering. No logical person would argue "this is better than {#if}{:else}{/if}".
This might be just a rationalization. React might be "winning" (whatever that means), because it was the first proper component-based library, when its competitors (Backbone, Angular, Ember) were large, slow, and clumsy, and still struggling with the concept. Plus, developers back then were easily impressed by the word "Facebook", which meant a large tech company that probably knew what it was doing, and served as a guarantee of good quality. So it had a tremendous head start and good marketing. If Vue, Svelte, Solid, or Lit were there first, who knows if React would be "winning" now.
If you want to write HTMX / HTML in a componenty and functional way then HARC stack gives you that for Raku. I grant that Raku is a step change if you haven't seen it before and that there are some awkwardnesses in the example that could be ironed out.
You may prefer PyHAT, FastHTML (Python), HARM (Rust), GOTTH (Golang) and so on...
With that said, HTMX feels like the progressive enhancement solution I wish I had back when I was doing so with jQuery (and Prototype.JS even before that). I appreciate your solution, and definitely want to take a deeper look in the future. For applications, I still tend to use ASP.NET (Core, whatever you want to call the current open-source version of .NET). HTMX seems like a perfect solution to progressively enhance an application, while your library does far more for controlling how library code is written in JavaScript than a gigantic mess of jQuery scripts with shared state.
As long as you're not invoking a hook in those if/else blocks!
In Svelte you are writing XML with little JavaScript islands inside it. Instead of if you get `{#if}{:else}{/if}`. Thats not "ergonomic" – thats a mini-language stapled on top of JS. No one wakes up saying "please let me write control flow in XML today"
The compiler tricks are neat, but React feels natural because it never asks you to stop writing JavaScript.