![]() |
|
![]() |
| Thank you for posting this! VanillaJSX is refreshingly different, and we desperately need new ideas in the front-end space to reduce the complexity and get closer to the browser. I also feel like the discussion in this thread is very rich and gives people on both sides of the fence a lot of stuff to think about.
There were two groups I was hoping vanillajsx would resonate with. The first is people who still buy into the React dream but are beginning to be disillusioned with its inability to deliver on its promises, and the second is people who already are fully disillusioned. I don't know if you've seen it, but Alex Russell just did a blog series where he directly talks about this disillusion and proposes a move away from React for most web apps: https://infrequently.org/series/reckoning/ I am not as anti-React as that myself, but I do agree it is hard to scale up and have it perform well, not at all like the promise. As always, there are no silver bullets and you have to pick a stack that you can understand. By the way, I made my own pitch for fully vanilla web development here: https://plainvanillaweb.com/ |
![]() |
| The "performance angle" isn't really an angle. It gets bandied around by junior devs new to React, but it's not the primary selling point of React - in fact, it's not a selling point at all. Don't believe me? Just go to http://react.dev and look - no where on the site does it say that React is a hyper-performant library. It's not! If you need blazing performance, you're best off using something much more minimal, or even vanilla JS.
When people say that React is fast, what they mean is that React can dom-diff faster than a naive O(n) approach. It means that updating a component with a thousand nested divs won't crash out your browser, like it might if you were to write the code by hand. It doesn't mean it's an objectively high-performing framework. What React is good at is forcing you to write code in a clear, comprehensible way. Having every engineer on your team obey F(props) = state is a strict improvement over virtually any other paradigm. (Yes, you can still make a tangle of components if you try hard enough, but the complexity of the tangle is capped significantly lower than the complexity of a tangle of JS without any framework attached.) |
![]() |
| You're technically correct (the best kind of correct), but it might be worth in an answer also exploring the possibility that they mean "Why is changing DOM a lot slower than changing VDOM a lot?" |
![]() |
| You can have JSX that produces DOM nodes or "light-weight element descriptions".
You can have imperative event listeners and updates. These are two independent dimensions. I made UI framework called mutraction that produces real DOM elements from JSX expressions. It also updates any contents or attributes of those DOM nodes based on their dependencies without requiring imperative DOM interaction from application code. https://github.com/tomtheisen/mutraction Here's a click counter. `track()`, as you might guess creates a proxy so that reads and writes can be converted into dependencies.
|
![]() |
| TBF while Solid and Svelte don't use a VDOM on which they perform diffing, they still ultimately create a tree parallel to the DOM which is used to track dependencies. |
![]() |
| Stop the madness. Its stupendously simple.
As the user, not you the author of some code, make changes to the UI it will be via events. In the respective event handlers just update a big state object with the identity of the node and how it is changed. When the page loads simply read from that state object to recreate the respective node exactly how they were when the user modified them. That's it. Nothing more. Its how I wrote a full OS gui for the browser. It does far more than SPA framework, does it substantially faster, and requires only a tiny fraction of the code. Yes, yes, here come all the excuses, like: I can't code or it wont work in a team, because other people cant code. Stop with the foolishness. Provide some governance, preferably fully automated, around event handling and you have complete obsoleted your giant nightmare framework. I describe it here: https://github.com/prettydiff/wisdom/blob/master/state_manag... |
![]() |
| UI is not a pure function of state[0], "UI state" is relatively stable and does not have to be recreated constantly when data input changes.
[0] https://blog.metaobject.com/2018/12/uis-are-not-pure-functio... >you want to be able to reevaluate your templates repeatedly with new state No you don't. It is inefficient and increases complexity. You then have to extract and keep track of state yourself where the platform/UI components could have done much of this themselves.
|
![]() |
| My framework (imlib[1]) is actually more of a framework akin to Astro than what's showcased on vanillajsx.com, which itself is built with imlib.
It just runs your code in a node.js process, and translates your JSX expressions into jsx() calls. On the node.js side[2], jsx() returns a string from its tag/attrs/children. On the browser side[3], jsx() returns DOM elements. Combined with a little bit of architecture, it becomes something extremely well suited to creating static sites. I guess SSG is an outdated term now, maybe it's a framework? Or a platform? In any case, it seems to do something similar to Astro, but in a significantly simpler way. The only "bundle" it needs in the browser is /@imlib/jsx-browser.js [4] which in itself is just jsx-dom.ts (its impl is overridable by the "framework" user). And on the node.js side, it's implemented as a very small "vm" of sorts [5]. I'm not against Astro, I just get all the same benefit people here are saying Astro has, but with orders of magnitude more simplicity imo. I've used imlib to make a relatively large website [6], in fact imlib was developed as this website and extracted from it over the past year. I have absolutely no difficulty breaking down my site into various reusable and encapsulated JSX components, both on the ssg-side and the browser-side. Development time is lightning fast. IDE support is essentially automatic. The site loads instantaneously in the static parts, and as quickly as reasonable in the dynamic parts. [1] https://github.com/sdegutis/imlib [2] https://github.com/sdegutis/imlib/blob/main/src/jsx-strings.... [3] https://github.com/sdegutis/imlib/blob/main/src/jsx-dom.ts [4] https://vanillajsx.com/@imlib/jsx-browser.js [5] https://github.com/sdegutis/imlib/blob/main/src/runtime.ts |
![]() |
| Oh, but you are moving out of React. And svelto IMO is waaay friendlier and "sane" than "typical" react. Svelte reactive model (observables and computed) are very friendly and simple to use. |
![]() |
| Have you looked into lit-html?
Coming from Vue I was really surprised it does a lot of what Vue templating does, including attaching events, with just vanilla JS templates. And when you use VSCode lit extension, you get syntax highlighting and full type checking inside the templates. I learned about lit-html after a tweet from Marc Grabanski, where he said he used lit-html with vanillajs, not Lit. After some experimenting I found it works great and it seems like you are trying to solve something very similar. When you use the lit-html template package you can do basically evetything that is described in the Templates chapter https://lit.dev/docs/templates/overview/ ... without all the other abstraction of components that are part of lit-element. https://lit.dev/docs/libraries/standalone-templates/#renderi... |
![]() |
| Tagged template literals for html view templating is what I LOVE about Lit and lit-html. It’s JavaScript you can run natively in browser, no server or pre processor/build step necessary. |
![]() |
| Downvote me but tell me why. The example is using .onclick, .textContent, etc in a completely vanilla way. I'm just pointing out you can get all the way vanilla and it still works. What's the issue? |
![]() |
| I also made a UI library based on the idea of jsx template expressions that produce real DOM nodes. It also binds model objects to attributes, eliminating some of the imperative event handler boiler-plate. I think it's a great idea, but of course I would.
https://github.com/tomtheisen/mutraction It lets you do stuff like this.
|
![]() |
| VanJS deserves a mention here! https://vanjs.org/
Another interesting thing is that other JSX libraries like Solid.JS also return DOM nodes, and I love that this idea is gaining traction The closer we get to the platform we're using, the better. Being removed by layers of abstractions CAN be useful, but in practice, I haven't found a use for abstracting away the platform. (yet.) Maybe huge projects like Facebook benefit from this tho (which I haven't worked on) |
![]() |
| People didn't want it because browsers didn't support it (except FF, as you noted). Some of us had our fingers crossed that other browsers would pick it up. |
![]() |
| I think parent must be referring to Flex components. AS3 itself had an XML library which I recall being absolute hell to work with. The better way to send things over the wire with AS3 was with AMF. |
![]() |
| No, writing XML was the declarative part of Flex (like HTML), but AS3 had it’s own XML type so you could do things like this:
var data:XML = and then data was an object instance like you’d expect |
![]() |
| The technique I used here and in all my browser-side code is the exact same technique used by VS Code internally, and it scales very well. The only difference in my code is it's more concise than writing 10 lines to construct and setup a DOM element the typical way.
Honestly, the real interesting part about my framework is literally everything else. Returning strings from JSX on the ssg-side; being able to import raw source directories and manipulate string|Buffer at ssg-time; the extremely efficient and lightning fast module system I wrote on top of chokidar and swc; probably more I'm forgetting, but basically the JSX-as-DOM is only the most visually interesting part. But really just a party trick. [edit] Case in point: the source code to vanillajsx.com is extremely concise and clear and short, I literally wrote the whole thing today with zero deps (besides imlib), and the JSX-as-DOM demos are the least innovative part of it: https://github.com/sdegutis/vanillajsx.com/tree/main/site |
![]() |
| Does the final example not work in Firefox for anyone else? It worked in Edge, but not Firefox for me
|
![]() |
| I've wondered the same thing. I think one benefit is that it looks like HTML, which means it looks similar to what you see in the browser's DevTools, which makes it easier to compare and debug. |
![]() |
| This question is crucial to understanding the true value of React and virtual DOM technologies.
While there's no doubt that React and virtual DOM offer advantages, it's essential to clearly demonstrate where and how these benefits manifest in real-world applications. > they're not showcasing cases where the performance gains are perceptible According to this commenter, it's not even about the performance gains: https://news.ycombinator.com/item?id=41271272 > and IMO not even cases where the "organizational" benefits of such libraries are salient Apparently, that is what it ultimately boils down to: |
![]() |
| Why wouldn't one be able to tell syntax highlighters and code checkers that the string that goes into the html.something() functions is html? |
![]() |
| There is a library for Python called htpy that does this.
Trouble is if you're used to HTML it can take a while to get used to it. It's like a learned helplessness or something. |
![]() |
| I don't see why the type casting (as HTMLButtonElement) is needed. Because document.createElement("button") returns HTMLButtonElement in TypeScript. |
![]() |
| It's very strange that when I land on the page for the very first time, I land halfway down the page and I'm staring at a block of random code.
Not what you'd expect to see. |
![]() |
| A side question. The advantage of JSX I see is the ability to connect, declaratively, components. I find this very helpful in terms of understanding programs I write. I wonder if I use React not because of the virtual dom, but simply because of JSX.
So I would like to explore the ability to use JSX in non-DOM environments. react-three-fiber does this with Threejs, but then it is still React oriented. I found this article about parsing JSX https://blog.bitsrc.io/demystifying-jsx-building-your-own-js.... And I know babel has something that parses JSX. Does anyone have recommendations for doing this. Threejs to me a good candidate - a non React version, since it is a hierarchical system (scene, meshes, materials etc), but I suspect there are other applications. I made an attempt to implement a Javascript version of Hickey's transducers - a sort of conveyor belt of functions and that is another instance of a series of processing steps that might be best represented in JSX |
![]() |
| I used to explore similar stuff and prototyped something I call “Vanilla Components” but then in the end I fell in love with Web Components and quit React (and all other frameworks). |
![]() |
| > but I think you always need at least a tiny runtime to avoid too much DOM access
Unless you use lit-html, which has a very efficient diffing algorithm that only updates the nodes that have changed. |
![]() |
| I’m having fun using vanilla js with lit-html. Using string templates instead of jsx. VSCode extensions for lit make it almost identical to editing vue templates with type checking etc |
![]() |
| Respectfully, my experience says otherwise:
- JS object appears to be at least 2x faster than document.createElement() (Chrome) - Note: JS object only loosely represents JSX element so it is a bit unfair. But with actual JSX objects I would assume it is still somewhat faster than the DOM API. https://youtu.be/DgVS-zXgMTk&t=1532 - Pete Hunt, one of the React devs, says "JSX is faster than the DOM because it is JS memory." |
![]() |
| Just out of interest I wanted to see something a little bit similar in Web Components:
|
![]() |
| You're not alone. Someone suggested that the W3C should convene a Community Group to discuss JSX, but the grown guys involved in writing standards immediately scrapped the idea. |
You want to return a description of the DOM, rather than the real DOM, because you want to be able to reevaluate your templates repeatedly with new state, and efficiently update the DOM where that template is rendered to.
All the examples here use imperative DOM APIs to do updates, like with this:
Avoiding those `input.onkeydown = ...` and `this.ul.append(item)` cases, and instead just iterating over items in your template, is probably the main benefit of a VDOM.(The problem with VDOMs is that diffing is slow, a problem solved by using templates that separate static from dynamic parts, like Lit - a library I work on).