I can remember why. This, and every other article I've ever read arguing to replace React with Web Components, completely misunderstands the point of React. It isn't about JSX. It isn't about encapsulation. It isn't about reusability.
It is about enabling a design pattern where *the user interface is a pure functional transformation of the application state.*
I kind of feel like people get tripped up by the fact that "virtual DOM" and "shadow DOM" sort of sound similar. They have literally nothing to do with each other. The React "virtual DOM" allows you to *completely re-output the entire user interface* on every state change, which is not possible with any other design pattern, and is not possible without a framework, because actually re-rendering the entire tree on every state change isn't performative (or usable).
Anybody who is advocating an alternative to React needs to do one of two things:
(A) Make a convincing argument that a different design pattern is better. Some things we've tried, which most people think are worse:
1. Using the DOM as your data model (jQuery)
2. Manually writing virtual representations of every view (Backbone)
3. Auto-magically two-way binding some data structure with the DOM (Angular 1)
4. Observables (Ember, maybe Angular 2+?)
(B) Advocate a framework other than React that uses the same pattern as React, but improves the usability. I think the two places there is the most room for improvements are:
1. Animations
2. useEffect() in general
I have not seen anybody successfully do either A or B.
Recommended reading: https://acko.net/blog/get-in-zoomer-we-re-saving-react/
(Admittedly, these benefits haven't been thoroughly researched, as far as I'm aware, so it still mostly relies on gut feeling and experience.)
This is only true for read-only components.
There is no "pure functional" when you introduce state and interactivity. See here: https://mckoder.medium.com/why-react-is-not-functional-b1ed1...
This is rarely done, because there are pragmatic reasons (e.g., animations) not to, but it is possible.
The other mistake alternatives make is to try to make components having internal state "easier." It should not be easier! Every single useX() is a statement that "I am violating the proper design pattern of this application," and it's a feature not a bug of React that you have to be obvious and intentional about it.
useEffect should have been named useDangerousSideEffectAndThisIsNotALifecycleHook and the js influencers should have never compared useEffect to component lifecycles and the React team should have updated their docs and not wait 4.5 years to do so and the dependency array should absolutely not be optional.
To achieve that, it doesn’t matter whether the state is local to a component or global across the application.
That works for simple, read-only components. The moment the component starts managing its own interactivity you end up needing state, and at that point things break down. If you need to change props, you have to essentially recreate the component by changing key [1], and at that point, what is the benefit of React?
[1] https://legacy.reactjs.org/blog/2018/06/07/you-probably-dont...
It's decoupling those two that brings all the power.
But the idea doesn't represent at all the web. So react is a mess of complex code trying to make the things you can create on the web behave like stateless pure components. Yet, it mostly works, and the react does indeed implement the idea that the interface is a pure functional transformation of the application state. (But I do disagree on claiming this the "entire idea", it's half of it at most.)
Don't forget Knockout which was the OG. You can also make the case that Svelte, Vue, and Qwik all are different takes on Observables (at least as much as Angular 2+ is) all with more or less magic and more or fewer escape hatches from Observable best practices to imperative(-looking) code.
I got a "What if we did Knockout but with with the compile-time benefits of TSX and Pure RxJS Observables?" itch a couple months back and have made some wild progress on it. I certainly don't think it is ready yet to advocate as an alternative to React, but it's probably in an interesting A4+B2 tangent on your map right here, and I find that interesting.
It was something that was also attractive about early React that React was similarly just a view engine and could do MPA or SPA or things in between and it didn't try to have the full kitchen sink. I don't know exactly where React stopped being that, but it always feels harder to argue that current React is "just" a view engine.
In the larger context here with this article, libraries that are just view engines should be great for building the internals of Web Components. (I'm hoping the view engine I've been working on might serve that role well, though with a dependency like RxJS I'm not sure if it would be towards the top of the list for many developers. It will certainly feel bigger than Lit, for example, no matter how well it tree shakes.)
> I probably would not choose to use javascript or observables/signals.
This seems to be a case where we differ. In one obvious part because I don't tend to like signals and think they are very different from observables (because they are harder to encapsulate and offer fewer operators and tend to bleed too many accidental imperative escape hatches). Observables, to me, are just as useful to describe "open this file, read its contents, convert it from Markdown to HTML, then bind it to innerHTML here" on the server side as "fetch this URL, read its contents, convert it from Markdown to HTML, then bind it to innerHTML here" on the client side. Very different forms of interactivity, but there is still an interactivity there that a good observable pipeline can describe. Especially when you start to get into the idea that the difference between "open this file" and "fetch this URL" is tiny and can be dependency injected. Same component, slightly different inputs, slightly different expected outputs (you expect the server side binding to complete, whereas you expect the client side one to live for some time and continue to respond to different URLs from input).
But anyway, the more UI frameworks, the more ideas get out there, the better. I'm hoping that some day in my lifetime, web UI gets "solved" and I'm not willing to believe that React is the answer. Does your project have a name yet? I'll keep an eye out.
I'm calling my view engine Butterfloat, and I only just finished the first documentation pass, so be gentle, but feedback is very welcome: https://github.com/WorldMaker/butterfloat
> 1. Animations
> 2. useEffect() in general
I'm not normally one to "shill" a web framework, and I still mainly use (and love) React, but I'm curious if you've tried Svelte, because it definitely manages animations better than React, and has a different idea of state that's more ergonomic for many use cases (though I won't go so far as to say it's definitively "better" than useEffect)
So maybe some kind of "non-functional"/reused state issue exists regardless?
Simple Model-View-Controller pattern works well for JavaScript, just as it does for ASP.NET Core, JSP and JSF, Ruby on Rails, Django and so on.
Want proof? Browse this code:
https://github.com/wisercoder/eureka/tree/master/webapp/Clie...
This is the application: https://github.com/wisercoder/eureka
In fact back then, everyone’s big app was MVC - usually Backbone. I worked on Airbnb’s host-side web app called “Manage Listing”, it had probably 30+ models, 150+ views, 100+ controllers in Backbone. There were many bugs in this scale of app around making sure the DOM reflected the latest change to some model. the engineering team regarded the more complicated screens as a nightmare to maintain in our fast paced environment with many teams touching the code.
Engineers at Airbnb started to adopt React as a solution to the problems we all experienced with MVC - not because it was a “hot new thing”. When React came out, it was widely regarded as weird - it was more like “eww, this smells of PHP and needs a weird compiler, but pure function of state is a lot better than fiddling DOM manually…”. Eventually we replaced Manage Listing backbone views with React components one by one until there was no backbone left.
React is still kinda weird but it does solve this problem the best out of the modern frameworks. It’s happy to over-render by default and prefers a correct DOM-for-state over everything else. It’s clear thought that the mental models popularized by React are worth it - now Apple and Google are switching to the React model in their own frameworks.
> There were many bugs in this scale of app around making sure the DOM reflected the latest change to some model. The engineering team regarded the more complicated screens as a nightmare to maintain in our fast paced environment with many teams touching the code.
I've seen this same issue in Cocoa/UIKit/Android views. Older Cocoa in particular has a lot of hairy wiring up of signals and first responders and delegates. I think it's less of an issue there because the rate of change is typically lower; in web land we expect to deploy continuously and with a very high number of engineers, anything targetting the app store will usually deploy at most weekly (2 orders of magnitude fewer deploys) and with many less engineers (usually an order of magnitude at least. My hypothesis is the difference in change rate is why this kind of bug was more of an issue for web developers.
Personally, I have not run into large amounts of bugs of this type, and I have written plenty of MVC code using VanillaJS.
Nothing will ever beat the following as tech demo for how easy it is to make interactive UIs, but anybody who ever tried to reason through a large Angular 1 application would seriously hesitate to want to use it for something much more complicated.
<script src="angular.js" /> <input ng-model="inputValue"> <div>{{ inputValue }}</div>
Just don't change application state from a component lifecycle function and you're golden.
Personally, JSX is the mainly thing that makes React attractive. As JSX is just modern E4X. It really ought to be put into the ES standard again instead of this Web Component stuff.
Another good JSX implementation (SSR) is: https://nanojsx.io
If you just want something light JSX, then this seems the way to go for now.
It can't be that that design pattern because React did not invent it. Every server-side templating library is a pure functional transformation of application state. I can tell you that many people also ported that design pattern to JS.
What tripped library writers up is once they applied that pattern to JS, there is no way to actually define the transformation because to do that, you either write a template or the code-equivalent (i.e. using DOM as your data model). And that's where JSX came in.
Also, I think you have patterns completely confused.
Backbone is NOT in the same class as jQuery or React. Backbone is more like Redux, mobx, or React hooks because it's a data model library. In Backbone, you define "models" and "collections" -- basically, you use Backbone to store your application data in memory.
Backbone did have a views component to it but it actually could not generate any HTML. Look at this example from their docs:
var Bookmark = Backbone.View.extend({
template: _.template(...),
render: function() {
this.$el.html(this.template(this.model.attributes));
return this;
}
});
It literally has jQuery and underscore.js! Backbone had no templating/HTML generation ability so when you used Backbone, you combined the fat Backbone classes with whatever horrific HTML-generation method you used to create a huge monster.You can actually use Backbone.js with React since they are not in the same class. You would not, of course, because mobx is basically the same style of data modeling as Backbone.js but with almost no boilerplate.
- not a framework, but a library
- can do fine gained state updates without unnecessary re-renderings (reactivity)
- uses dom instead of vdom to produce best in class SPA performance
- uses jsx like react
Performance claim / proof: https://krausest.github.io/js-framework-benchmark/
But Lit templates solve these problems in a more browser integrated way, without the need of a virtual DOM. How you manage the state is free to your choice, that is also not something exclusive to React and your favorite pattern can also be used with Lit. I wrote a tiny state management library (LitState [0]) which makes it very easy for multiple components to share the same state and stay in sync. I personally find it much more convenient and cleaner than any other state library I've used before. And it integrates very nicely with Lit.
How many times have you seen the equivelent to a fullname = firstName + lastName inside a useEffect? Its outstanding. :(
Everything else, even if it has a better technical fit for your project, is likely riskier for your organization.
If I was going to learn a language though, I'd probably learn Golang. Their rate is so much higher.
We're hyper-specializing people in a specific framework that then don't even understand the basics of JavaScript itself.
I already find the divide between frontend and backend developers unnecessary the majority of the time. This takes it a step further and in turn creates monstrosities like the leftpad debacle.
Can we stop with this nonsense? Hire good developers and let them spend 5 minutes to learn the god damn framework of the week. If your hire can't learn React in a week, I have some bad news for you.
Doing something like this.shadow.innerHTML = `your HTML` with web components is terrible. document.createElement() is terrible. $('div').appendChild() is terrible. <script language="html" name="my-template"><!-- --></script> is terrible. HTML(Div(Strong(text))) is terrible*. Storing templates as JSON and writing a one-off loader is terrible. <div style="display:none">template</div> is terrible. I've done it every possible way and they are all terrible.
JSX fixed all that.
The biggest previous attempt at something like JSX was E4X where you could embed XML straight into JS, which was kinda nice except it added the entirety of XML and its complexity. I creamed when it came out but then I tried it and it was not it. (E4X ended up surviving a little longer in Adobe Flash though.)
E4X: https://en.wikipedia.org/wiki/ECMAScript_for_XML
*This is what JSX compiles to behind the scenes.
This all feels rather half-baked. Why can't the parser/prprocessor distinguish between that "class" and "class" in JS source code? We can have reusable HTML snippets (some may call components) with normal templating engines easily. Look at something like Jinja2 and how to reuse blocks and macros. Yes, some React component can encapsulate its own interactive behavior. That is only because we are already writing JS though, so we can already write frontend logic. But do we actually want that? Coupling state and behavior? I think we might not. Writing a script that gets served only on those rendered templates, where it is needed is not so hard either, when using a normal templating engine.
React is simple. Here is your data, and here is a function that transforms that data into a view.
Templates are terrible, and that is a hill I am willing to die on.
Is that aspect so much different from templating engines? In templating engines the rendering step is simply abstracted one step further: You get a generalized render function, that you pass data to and a template to render with that data. That is also a function to turn your data into a view. I don't see how React is better in that aspect.
> This all feels rather half-baked. Why can't the parser/prprocessor distinguish between that "class" and "class" in JS source code?
Remember that React is "just JavaScript", JSX is a syntax transformation of JavaScript. When you use `className` in your JSX, you're using the standard DOM Attribute [1]. You wouldn't use `class` either in vanilla JS to set the CSS class, you'd use `className`.
https://developer.mozilla.org/en-US/docs/Web/API/Element/cla...
In a usual templating engine valid HTML is also valid template. One does not have to use templating engine specific things. A HTML snippet can simply be HTML. No harm done. But in JSX one cannot use normal HTML, as shown by the `class` case.
> However, that means, that now one has something, that is neither looking like HTML, nor is it looking like JS, even if it may be JS through some pre-processing (but then actually it is not really JS?).
Yes, it's not HTML or JS, it is JSX. It's an XML-based syntax to declaratively write JavaScript. It's optional too - one can write the function calls directly in JS, and indeed that's what people did before JSX existed. `React.createElement("div", React.creatElement("span"),...)`. It could be worth using a REPL [1] to gain an intuition of the JavaScript that is produced from transforming different JSX examples.
> The result is, that one has to learn some different syntax that is specific to React.
JSX was originally created for React, but now is not specific to React, other libraries and frameworks use it as well. It's a general purpose XML-based syntax for writing JavaScript declaratively.
And yeah, if you want to use JSX you have to learn it. It's easy to learn if you know JavaScript and XML and shouldn't take very long. It's XML and then anything inside curly braces is a JavaScript expression.
If you're using TypeScript replace everything "JS" I'm saying with "TS", it's the same result. And obviously type checking works without issue because it's a syntactic transformation to TS, not a semantic transformation.
> In a usual templating engine valid HTML is also valid template. One does not have to use templating engine specific things.
JSX is not an HTML templating engine [2]. JSX is a way to write JavaScript declaratively using an XML-based syntax.
> But in JSX one cannot use normal HTML, as shown by the `class` case.
Yes, as expected. JSX is not an HTML templating engine, it's a way to write JavaScript declaratively using an XML-based syntax. In JavaScript, you use the DOM APIs [3], not HTML attributes [4].
[2]: https://legacy.reactjs.org/docs/introducing-jsx.html
[3]: https://developer.mozilla.org/en-US/docs/Web/API/Document_Ob...
[4]: https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes
But you know, if you are trying to put HTML templates in separate files, you have to now send extra HTTP requests. That's an even bigger no no.
In other languages, you can just put your template in a separate file and load it from your package/.jar/assembly. Super easy and most importantly, 100% standard and provided by the environment.
So people just settled with React because it worked and no successfully invented a standard bundle format for the web.
F' it we said.
It's not actually, with HTTP/2. The separate requests are all multiplexed over the same connection, often with prefetching, and compressed together so that there's little to no overhead vs. putting them in the same file.
This illustrates a common failure point for web frameworks though. They encode a lot of folk knowledge about how the web works, but that folk knowledge becomes obsolete as browsers change.
Another prominent React example fits into this category: the virtual DOM. React assumed that DOM manipulations were slow and Javascript manipulations were fast, and so they built up an internal representation of the DOM in Javascript so they could manipulate that one and diff the change to apply it to the real DOM. This was true from approximately 2008 (when V8 introduced JS JITting) to 2013 (when Blink/Webkit/Firefox all moved to a dirty-bit system for DOM manipulations), which not coincidentally is when React was designed. But since then, DOM manipulation has been just pointer swaps and a dirty bit flip, and if you're careful not to invoke any operation that triggers a reflow and repaint you can make your modifications directly in the DOM for roughly the same cost as making them in JS. Since React is declarative it could easily have enforced the no-reflow rules without the virtual DOM, but because DOM manipulations were expensive when its designers learned web programming, it introduces an unnecessary concept.
But HTTP/2 is not the right example. You're missing some things on the web timeline.
Long before HTTP/2 (time in web terms), and after React (and Angular, etc.) came about, people created actually-popular bundler toolchains that can bundle arbitrary files together and then incrementally load them as needed. This effectively fixed the multiple HTTP request problem long before HTTP/2 existed. Efficient template files have been possible for years. (Granted, not anything standard though.)
The issue right now is that someone has to make a full toolchain with strong developer support to make this a de-facto and popular way to distribute apps. Just because you have a bundler or HTTP/2 doesn't mean you have a nice way to make use of it. You need a nice library. Your usage examples need to look pretty. It needs to work with other toolchains. It needs to look like your library will be supported for a long time, just like how React was supported by Facebook.
You can't just write a blog post about how it's possible because you're basically asking your readers to invent and maintain a library or worse, roll their own internal library that will confound people at their company.
> What forces layout/reflow. The comprehensive list.
> All of the below properties or methods, when requested/called in JavaScript, will trigger the browser to synchronously calculate the style and layout*. This is also called reflow or layout thrashing, and is common performance bottleneck.
Slightly longer answers:
[modifies] https://developers.google.com/speed/docs/insights/browser-re...
[measures] https://gist.github.com/paulirish/5d52fb081b3570c81e3a
Also there's a bunch of exceptions where you can modify things without triggering a full reflow. Anything that takes elements out of normal flow (position: fixed, position: absolute, float:, overflow: hidden) creates a layout boundary where changes inside only reflow the containing element. Any element with a transform: or opacity: property gets rendered as a 2D texture on the GPU, and then you can do subsequent transform/opacity animations purely on the GPU without touching the CPU. Used to do this to make performant animations on mobile devices.
Most projects that I have been involved with have some kind of build system that does all sort of magic. It should be possible to include the template in the JS file during the build process
Here's an example of using a web component in a JSX template (look for zx-listeditor): https://github.com/wisercoder/uibuilder/blob/master/WebCompo...
Here's how the web component is implemented, also using JSX: https://github.com/wisercoder/uibuilder/blob/master/WebCompo...
{ tag: 'div', padding: '2px', position: 'absolute', content: [myText] }
than <div style="padding: 2px; position: 'absolute'">${myText}</div>
The main reason I ended up settling for JSX was just convention: I don't want to be the one with my own unique format for UI trees unless I can convince everybody else to use my format.Note that Jetpack Compose has a very similar declarative reactive paradigm as React, but represents components with Kotlin language mechanisms. IMHO I like it much better, because it's much more composeable.
It's new and has no significance for the history. But I think it's relevant to point out that <template id="myhtml"><div></div></template> isn't horrible.
And that those articles about web components are doing a really bad job of using constant strings instead of this.shaddow.innerHTML = getElementById("myhtml").innerHTML.
It's bad because it's inline and your templates have no obvious place to go. You also can't import them on the fly (or rather, it's as idiomatic to import as any HTML). And obviously, because they have no effect on HTML and can only be used in javascript
Yeah it’s another framework with its own quirks but it lets me think about the UI in the same way I’ve thought about it for 25 years. For someone like me that’s a godsend.
[0]: https://lit.dev/
> This is what JSX compiles to behind the scenes.
Oh, it is much worse than that. By classic default it is:
React.createElement('div', { /* props and attributes ... */ },
React.createElement('strong', { /* ... */ }, text))
Recent updates and other JSX frameworks simplified the name of `React.createElement` to just `jsx` or `h`. I've seen projects that manually write `h` calls like that everywhere, and it is indeed terrible.(Source: I've just completed a bunch of crazy TSX work and got pretty familiar with the compiled forms.)
I was skeptical at first but I've seen in production a lit.js/WC component library used with React and Angular successfully in the banking sector and it works surprisingly well.
1. Eases candidate selection. There is no uniform baseline of competence in software generally and certainly nothing exists for web development. Its often the blind leading the blind, so outsource everything to a tool. At the very least, people that cannot use that tool are then not qualified to be there if you discount absolutely everything else. After all, it isn't as though employers are willing to train new developers to perform as required, so they need to turn this into a commodity as much as possible.
2. Composition. Most of the people who do web development cannot write original software, plan, or organize at the level required by modern applications. Employers still need people to do trivial things to get text to appear on screen. As the demand for these trivial tasks still exists employers still need to hire people to do this work, and so they attempt to outsource the parts that require higher intelligence.
These beg the obvious question: What will happen when employers realize they don't need to overpay developers to do this work when half of it can be pushed into content management systems and the other half can be pushed into AI?
Back in reality, React is slow and massively overbuilt for the majority of stuff that most people are building.
If you played around with Web Components a long time ago then you probably played with the v0 spec, which was somewhat different to the current version.
If React won, it was precisely because it was not "overbuilt."
It was simpler (deliberately) than Backbone, Angular, Vue, Svelte, etc.
You can implement a React clone in a few hundred lines, a la Preact.
React won by being early and different. Svelte came out three years after React and got (unfortunately) steamrolled.
One is that the implementation is very simple/transparent, e.g. C.
The second is that it allows users to have very simple code, e.g. Python.
React is mostly the first with a bit of the second. Svelte is the reverse.
You do know that Google sponsored and ran things like Polymer conf? That it builds lit? That Google's devs have literally overrun and overruled most web specs committees? That Google has spend hundreds of millions of dollars promoting Web Components?
And yet here we are.
As for Web Component specs, we got v1 because of Apple and Mozilla: Google would happily have stayed with v0.
In retrospect, I think I avoided web components because of Google and their aggressive dev relations pushing Polymer. It felt so one-sided that it didn't seem like a web standard. It felt more like a Google "standard" like NaCL. Having to ship Polymer to support non-Google browsers felt even more heavy weight than React.
Then you should skip Web Components :)
1) boring, old technology that has been around for so long that no one even remember why it was chosen in the first place, and
2) extremely fast-churning, bleeding edge software that is constantly changing and breaking because the JS community is more interested in chasing trends than building robust stuff.
1) the ecosystem keeps chasing shiny tech that is constantly changing, not boring solutions
2) everything should be written in Rust
I prefer the terms stable, mature, or hardened.
People use React/Angular because large products are forced to have a way to organize things across multiple developers and time. Now don't get me wrong I think these JS bloated websites are an abomination that have largely made the web worse and not better, especially since UX "engineers" feel a compulsive need to change the layout every 6 months and now even simply scrolling through a page involves 50 web requests, and takes a full 3 minutes to load the next page... but I digress.
They are still messy. https://w3c.github.io/webcomponents-cg/2022.html
I think that is the point. The original "pitch" for react in my mind was that it was lightweight and simple. I don't think you can say that any more, and I think a lot of rough edges have been identified (e.g. hooks fiasco, app state management, dependency-hell etc to name just 3) after years of use.
It's the same cycle that always happens. Old thing ossifies and grows fat overtime trying to be all things to all people. Then some new thing appears that starts with a clean slate and no prior expectations that proves popular as a result
I can easily justify the reasons I still use React, but I can't be bothered writing it out every time some gimmicky front-end tech hits HN.
1. Go back to reactive templates.
2. Sprinkle directives over plain HTML a-la early Angular 1.
3. HTML over the wire.
All those ideas pre-date React and React won against them back in 2014.
4. The React model but with compiler support to improve the ergonomics (and with better static checks).
> At this point React is legacy technology, like Angular. Lots of people are still using it, but nobody can quite remember why. The decision-makers in organisations who chose to build everything with React have long since left. People starting new projects who still decide to build on React are doing it largely out of habit.
I'm going to withhold my own thoughts about that quote, cause I'm curious what everyone else thinks.
Wrt. Web Components, I played around with Web Components about half a decade ago and they felt promising but not ready for prime time yet. They were pretty messy and felt like they did a poor job of encapsulation. (At least at the time.) I haven't looked at them recently, but the code examples in these articles look much better (and cleaner) than what I remember.
[1] https://adactio.com/journal/20618