VanillaJSX.com

by novocanticoon 8/16/2024, 7:01 PMwith 225 comments

by spankaleeon 8/16/2024, 10:59 PM

Returning actual DOM nodes entirely blunts the big advantage of JSX (and non-JSX libraries like Lit) - which is their immediate mode style API, and UI=f(state) model.

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:

    function TodoInput(attrs: { add: (v: string) => void }) {
      const input = <input /> as HTMLInputElement;
      input.placeholder = 'Add todo item...';
      input.onkeydown = (e) => {
        if (e.key === 'Enter') {
          attrs.add(input.value);
          input.value = '';
        }
      };
      return input;
    }

    class TodoList {
      ul = <ul class='todolist' /> as HTMLUListElement;
      add(v: string) {
        const item = <li>{v}</li> as HTMLLIElement;
        item.onclick = () => item.remove();
        this.ul.append(item);
      }
    }
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).

by novocanticoon 8/16/2024, 10:54 PM

Thanks for taking some interest in my project. It came from being frustrated with the state of SSGs over the past 10 years. I mostly just make static websites, and I wanted something that was simple and intuitive to me, and JSX seemed like a great fit. But I got very tired of the disproportionately scaled complexity of JSX frameworks like React. Long story short, I made an SSG that just renders JSX as strings. It was natural to extend that to the browser to just render JSX as DOM elements. And in a few cases (mostly layout) it lends well to shared components. Overall I'm happy with what I came up with, although some of it is admittedly a little hacky, and IDE support isn't as good as it could be.

[edit] Oh also, this solution works really well for SEO. That's another problem I didn't find solved well in other JSX frameworks.

by cribbleson 8/16/2024, 10:37 PM

These "what ifs" are kinda funny because the origins of JSX can be traced back to Facebook's XHP[1], which took explicit inspiration from E4X[2], an early JS standard that looked and behaved similar to the library described here.

[1] https://engineering.fb.com/2010/02/09/developer-tools/xhp-a-...

[2] https://en.m.wikipedia.org/wiki/ECMAScript_for_XML

by recursiveon 8/16/2024, 11:08 PM

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.

    const model = track({ clicks: 0});
    const app = (
        <button onclick={() => ++model.clicks }>
            { model.clicks } clicks
        </button>
    );

    document.body.append(app);

by merlindruon 8/17/2024, 7:30 AM

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)

by sophiebitson 8/16/2024, 10:34 PM

These examples are cool but I think it’s important to note that none of them show components whose props can change over time, since that ability doesn’t seem to be modeled at all. Clever if you don’t need that but I’m having trouble seeing how it would scale to more complex apps.

by flowerladon 8/17/2024, 12:17 AM

This is very similar to Vanilla TSX: https://github.com/wisercoder/uibuilder

Here’s an app written using Vanilla TSX: https://github.com/wisercoder/eureka/tree/master/webapp/Clie...

by config_ymlon 8/16/2024, 10:10 PM

Reminds me of Action Script 3 which had XML at the core of the language. It was a fun language to work with, but famously failed to become ES4. Oh well, took us 10+ years to arrive close to that with Typescript and JSX.

by girvoon 8/17/2024, 2:30 AM

Does the final example not work in Firefox for anyone else? It worked in Edge, but not Firefox for me

    Uncaught (in promise) TypeError: Map.groupBy(...).entries().map is not a function

by slmjkdbtlon 8/17/2024, 12:47 AM

I never understand the appeal of JSX over something like

  h("div", {}, [
    h("p", {}, "this is easy"),
    ...list.map((l) => h("li", {}, l),
  ])
With this you automatically get loops, variable interpolation etc without having to invent a compiler and new syntax. Can someone help me understand?

by ibashon 8/16/2024, 10:34 PM

People forget what problem the virtual dom and react is supposed to solve.

No better article than this: https://blog.vjeux.com/2013/javascript/react-performance.htm...

by hizanbergon 8/17/2024, 5:41 AM

Anyone else used Hono with SSR JSX? [1]

Was super productive and easy to create a Cloudflare Worker Web App that’s free to host thanks to Cloudflare’s generous 100k daily worker request limit.

Generally don’t believe in serverless for larger Apps, but for small websites that you just want to create, deploy and ignore - it’s great!

https://hono.dev/docs/guides/jsx

by mgon 8/17/2024, 6:52 AM

What is the benefit of mixing js and html?

    el = <button>Click me</button> as HTMLButtonElement;
What would be the downside of

    el = html.button('<button>Click me</button>');
?

That way no compilation step would be needed and debugging would be easier as the code executed in the browser is the same code the developer writes.

by EugeneOZon 8/17/2024, 4:36 AM

As often happens with minimalistic approaches, it only looks interesting on very small and very simple examples.

After “How would they handle large data?” it turns into an unreadable mess.

Communication between elements is not covered, global deps, DOM updates scheduling, content projection, and so on - you “just don't need it” in small demo examples, but you do need it in the real apps.

by drikerfon 8/17/2024, 6:29 AM

Nice project! I do wonder though if jsx is the best way to represent elements in code?

Clojure datastructures makes this so much more enjoyable. Everything is just basic lists and maps which makes it very flexible and powerful.

[:ul [:li "task 1"] [:li "task 2"]]

It's weird that it's not more common for making web apps.

by whazoron 8/17/2024, 6:54 AM

I don't see why the type casting (as HTMLButtonElement) is needed. Because document.createElement("button") returns HTMLButtonElement in TypeScript.

by ilrwbwrkhvon 8/17/2024, 3:16 AM

Imba is what anyone interested in this sort of thing should look at. I have no idea why it is not more popular. Maybe because JS devs falls for Faang marketing easily.

https://imba.io/

by andrucon 8/17/2024, 1:36 PM

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.

by talkingtabon 8/17/2024, 12:33 PM

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

by andrewstuarton 8/17/2024, 12:46 AM

Just out of interest I wanted to see something a little bit similar in Web Components:

    <html lang="en">
    <body>
      <h1>Web Components Examples</h1>
      <h2>Counter Component</h2>
      <counter-component></counter-component>
      <h2>Clickable Button Component</h2>
      <clickable-button></clickable-button>
      <h2>Toggler Component</h2>
      <toggler-component></toggler-component>
      <script>
        class CounterComponent extends HTMLElement {
          constructor() {
            super();
            this.count = 0;
            this.button = document.createElement('button');
            this.button.textContent = this.count;
            this.button.addEventListener('click', () => {
              this.count++;
              this.button.textContent = this.count;
            });
            this.attachShadow({ mode: 'open' }).appendChild(this.button);
          }
        }
    
        class ClickableButton extends HTMLElement {
          constructor() {
            super();
            this.clicked = false;
            this.button = document.createElement('button');
            this.button.textContent = "Click me!";
            this.button.addEventListener('click', () => {
              this.clicked = !this.clicked;
              this.button.textContent = this.clicked ? "Clicked!" : "Click me!";
            });
            this.attachShadow({ mode: 'open' }).appendChild(this.button);
          }
        }
    
        class TogglerComponent extends HTMLElement {
          constructor() {
            super();
            this.on = false;
            this.button = document.createElement('button');
            this.button.textContent = "OFF";
            this.button.addEventListener('click', () => {
              this.on = !this.on;
              this.button.textContent = this.on ? "ON" : "OFF";
            });
            this.attachShadow({ mode: 'open' }).appendChild(this.button);
          }
        }
        customElements.define('counter-component', CounterComponent);
        customElements.define('clickable-button', ClickableButton);
        customElements.define('toggler-component', TogglerComponent);
      </script>
    </body>
    </html>

by NohatCoderon 8/17/2024, 6:16 PM

For anyone who can live without <> syntax I made DOM Maker, no compilation step, no injection vulnerability footguns, just make a bunch of function calls in a tree structure, and you get DOM with the same tree structure, complete with non-string event handlers.

Mostly I just do Vanilla.js, but the vanilla DOM creation functions turn really verbose, I got tired of that and created this to cut back on code size and increase readability.

There are other libraries that do something similar, but in my own very biased opinion this is one of the better.

https://github.com/NoHatCoder/DOM_Maker

by cieson 8/17/2024, 10:29 AM

I frown at JSX. Just a layer of abstraction that is so "leaky" that you have to know what actually goes on in the layers below or you are fucked.

It looks simpler at first glance/ to a untrained eye; but it's just adding complexity without really solving any problems.

I like approaches like Kotlinx.html, scalatags, Elm's HTML package or HtmlFlow. They are also abstractions, but they add typesafety that html-as-a-string does not offer. On top of that you get breakpoints, code completion, and you can keep working in one language.

by miikaon 8/17/2024, 7:05 AM

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).

by spullaraon 8/16/2024, 10:43 PM

I was bummed when they removed E4X from the browser implementations.

by arjvikon 8/16/2024, 10:07 PM

What benefit does the virtual DOM add?

by xwallon 8/17/2024, 10:09 PM

No matter how complex your app is but still React will not break, performance on web is not a big issue as benchmarks say, even a junior developer can achieve 90%+ lighthouse score, but any senior developer may fail to ship it successfully.

ultimately go to react.dev because: "Maturing is realizing React is best"

by dqhon 8/17/2024, 8:58 AM

Those interested in this space may find my fairly unknown project interesting: https://nakedjsx.org/

It started as a static site generator but added a bunch of support for client JavaScript too.

by NaN1352on 8/17/2024, 10:27 AM

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

by waynenilsenon 8/16/2024, 10:27 PM

This plays very nicely with the locality of behavior model of htmx

by n3stormon 8/17/2024, 9:31 AM

For me is like old PHP where HTML and controlling and data access was all around. We use to call it spaghetti code.

by cyanydeezon 8/17/2024, 12:10 AM

I just don't understand how people can configure their brains to parse html inside JavaScript

by emaddaon 8/17/2024, 2:32 PM

One of the reasons for JSX originally was to reduce usage of the DOM APIs, as they are slower than direct JS object manipulation. The JSX diff of prev/next allows you to minimize DOM API calls.

I would guess there is more overhead in creating a dom element than a JS object (which JSX elements compile to).

by nf17on 8/17/2024, 9:46 PM

Great job, is there something similar but for SwiftUI?

by nashashmion 8/16/2024, 10:56 PM

I wonder what The examples would look like in ECMAscript 5.

by NaN1352on 8/17/2024, 10:24 AM

How does this stack up aginst using lit-html?

by andrucon 8/17/2024, 1:39 PM

Any comparisons on performance?

by frabjousedon 8/17/2024, 5:08 AM

It’s already solved. It works well. Just walk away.

by jwtorreson 8/17/2024, 8:03 PM

I genuinely don't understand why anyone would be interested in using frameworks on top of JS. None of them can do anything that pure JS can't do (+libraries), they just make it less readable and less intuitive compared to the original C-like syntax of JS. JS libraries make sense, of course, but why keep messing with the syntax?