As someone who does remember why because they've been building sites since the 90s, it's because the React team created a preprocessor (JSX) to let you embed HTML tags in JavaScript. This fixed the problem that other languages like Java, Python, etc. don't have because those languages has assemblies/packages so they can put HTML templates in separate files and load them in your app. There is zero standard way to do that in JS and every custom way you invent looks like trash.
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.)
Interesting. Yet I find that React HTML—that is not actually HTML, but a look-alike, a dialect if you want—to be exactly what I dislike about React. That is because it still encourages people to put JS in their HTML in their JS in their ... And we are almost back to crazy PHP land of "I treat HTML as a string and blend everything together into one big lump.", instead of using a templating engine, that treats HTML in separate files, or making use of a DSL, that treats HTML as structured data, say for example SXML. Also there are issues with that custom HTML-look-alike preprocessor, because you cannot write class="...", because then somehow it gets syntactically confused, because "class" is now a keyword in JS.
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.
Except it's not half baked. Because guess what - now you need a new template language that supports constructs like conditionals, loops, etc... You basically end up implementing a half-baked version of javascript with an unfamiliar syntax. Even worse they need to interact with state. Many template engines introduce some sort of "data binding" abomination which is a complex way of saying how you want the state to interact with the view.
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.
> React is simple. Here is your data, and here is a function that transforms that data into a view.
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.
Your criticism was the biggest one of React when it came out -- mixing view code with your controller code. Big no no. Everyone loved the MVC pattern (model-viewer-controller where you separate these things in different places) and what React did was a big no no.
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.
> 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.
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.
Multiplexing requests doesn't help when there is a logical dependency between them, e.g. some javascript code needs to download and then execute just to determine what template files it needs to subsequently download. That can't be parallelized.
If you're in a position where you could've written the template inline in your source file, this is a static dependency, independent of any page content, and can be handled through build systems and prefetching.
Short answer: anything that modifies the DOM, followed by a call which measures geometric properties of the DOM. Also you get an automatic reflow/repaint when control leaves the Javascript event handler that triggers a modification (this is how the browser's UI is updated). You can batch up a bunch of modifications without triggering a reflow and then have it account for all of them on next reflow, though, which is what React tries to achieve with the Virtual DOM and what you get for free after ~2013.
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.
"Generally, all APIs that synchronously provide layout metrics will trigger forced reflow / layout."
> 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.
>But you know, if you are trying to put HTML templates in separate files, you have to now send extra HTTP requests.
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
Yep, and that's what was being done before React existed. JSX took over because people (including me) prefer to have the full power of JavaScript for their templates rather than a half-based templating language.
> Also there are issues with that custom HTML-look-alike preprocessor, because you cannot write class="...", because then somehow it gets syntactically confused, because "class" is now a keyword in JS.
> 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`.
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?). The result is, that one has to learn some different syntax that is specific to React. If there is already a preprocessing step, why not preprocess `class` instead of `className`, to get closer to normal HTML syntax?
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.
I think you're hung up on JSX being HTML - it isn't HTML and doesn't try to be. The "JS" stands for "JavaScript" and the "X" stands for XML, not HTML.
> 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].
100% agree. Like you I’ve been building sites since the mid 90s and always was pulling my hair out at all the convoluted ways we would attempt to handle HTML in these JS frameworks. As someone who knows HTML and CSS and vanilla JS, as soon as I saw React all I could think of when I saw JSX was “finally!”.
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.
I remember using PHP and CodeIgniter in the mid-2000s. IIRC PHP had that template-interpolation thing solved since the first day: You populated your Views with PHP code.
Not saying that you can't. I'm just explaining why React won and how it was blatantly obvious that it was going to win when it came out (that is, if you were there in the before-times).
Agree with you on that. React was a breath of fresh air compared to what we had in the before-times, such as Angular, Ember and so on. The canonical demo of Ember.js was two-way data binding. React did away with two-way data binding and introduced JSX syntax. This was a major step forward. Code suddenly looked sane and readable. Then they took two steps backward when they introduced hooks. Code went back to being unreadable.
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.
The implementation is new but as far as the UX goes, it's very similar to <script language="html" name="my-template"><!-- --></script>. That said, the UX is going to be slightly better for <template> simply because it's is an official standard and so IDEs and tooling will be more likely to support it without additional extensions or plugins.
It's much more descriptive and obvious, and it's just plain HTML, so anything that assists you on writing HTML will assist you inside the templates.
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
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.)
Yeah, you're right. I was kind of simplifying it. API-wise, it's more like document.createElement() but I didn't want to asterisk that because document.createElement() is actually a thing and I didn't want people to think it was a simple layer on top of document.createElement.
I liked the templates-as-JSON approach much more than JSX. HTML is terribly verbose, doesn't fit well with the rest of Javascript, doesn't let you use JS language features like destructuring or iteration on the component tree, and doesn't let you factor out common properties into their own data structure. Would much rather see:
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.
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.