return to table of content

VanillaJSX.com

spankalee
45 replies
19h30m

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

francasso
16 replies
13h56m

From my experience creating complex web UIs, the performance angle of using a vdom is pure fantasy if your application is complex enough.

In fact I now strongly believe it's counter productive, because most people come to it thinking "I can just trigger however many re-renders of this large piece of UI as I like, the vdom makes it ok" and it doesn't, the performance sucks, but now you have architected the app in a way that requires a rewrite to make the app perform well.

I have seen this exact sequence of events four times, by four different teams. The second, third and fourth, as a principal architect consulting for the team I tried to intervene and advocate for a vanilla architecture that is mindful about performance, citing the issues they would likely experience with react, but to no avail. There was a lot of "oh but there many ways to avoid those issues" followed by a list of things I was presumably ignorant about.

I guess most of us need to learn things the hard way.

novocantico
3 replies
8h6m

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.

Specifically, I'm hoping to show that vanilla architectures can be not only performant, but easy to maintain with well designed code that uses stable and known patterns. Using JSX just so happens to clean up the code nicely and make the relationship between React and vanilla very visible, but that's really all it did here.

Although to be fair, the hack required to get JSX-as-DOM to work is really unfortunately and I'm not very happy with it, and I would prefer JSX to just render as an object tree that anyone can render however they want. But when I tried that for a few months or a year, it was not nearly as performant as rendering them as strings as soon as they're evaluated, which can then be cached via standard module caching. At least, that's how I got immaculatalibrary to entirely render all HTML files in ~700ms initially and ~70ms on most file changes.

I'll try to do some experimentation next week to see if I can get more performance back out of having <foo bar={qux}>child</foo> to render as {foo:{bar:qux, children:[child]}} again though, because that would absolutely be the ideal, and would unfork JSX in the same way Typed Annotations proposes to unfork JavaScript types.

Joeri
1 replies
7h22m

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/

merrywhether
0 replies
2h21m

IMO that blog series misses the point. Knowledgeable motivated developers can make great experiences with any technology, and conversely there are bad experiences built with every technology. That series blames the people involved for not being better, but that’s just blaming plane crashes on human error and calling it a day.

- If the UK GSD is anything like USDS, using them for comparison is like comparing a pro sports team to your local high school’s. They are an outlier specifically created to be better than the average, so tautologically their stuff will be better. Code For America is a similarly odd comparison.

- The US has a massive gap in pay and prestige between public and private sector developer jobs. It’s not that this means “worse” people work at public jobs, but in general they start less experienced and can wind up in a non-learning cycle as they don’t get mentorship/guidance from more expert folks, and if they do get good independently they leave. It’s really hard to convince people to take a pay cut to work these jobs, and many of the few willing to do so instead go to CFA, USDS, etc because they want prestige and avoid all the other inefficiencies in public jobs.

I could go on about the structural problems leading to this, but suffice it to say that blaming React and other JS frameworks is a miss. For some services it’s lucky they are online at all, and a slow web page is still orders of magnitude faster than physical mail or god forbid going to a physical office. The sites could definitely be better but this is not fundamentally a problem of technology choice.

no_wizard
0 replies
1h26m

You might find this project[0] interesting if you haven’t given it a look.

It was attempting to do something along the same lines as you first suggest

[0]: https://github.com/jridgewell/jsx2

unconed
2 replies
10h59m

Yes and the solution is to put on your big boy pants and to actually do your front-end application architecture properly.

Separate source of truth from derived data. Separate possibly valid user intent from validated state. Use contexts to organize the data dependency graph of your application. Ensure all your widgets use a consistent value type in and out, don't let events contaminate it. Use something like cursors or optics to simplify mutations and derive setters automatically.

I've never had an easier time building very complex UI functionality than with React. But it requires you to actively start reasoning about change in your code (what doesn't change), and this is something most people are not used to.

Personally I think React compiler is folly for this reason: they are taking the most interesting part of React, the part that lets you write apps that are incremental from top to bottom, and telling you it's too complicated for you to think about. Nonsense.

The issue is just that React makes pros feel like idiots unless they eat some humble pie and grok the principles and the reasons behind it. Which is that React is what you get when you try to come up with a UI architecture that can make entire classes of problems go away.

Without a VDOM, one way data flow, and diffing, your UI won't just be slow, it'll be full of secret O(n^2) cascades, random ifs to stop infinite cycles, random "let's update this state early so other code can immediately use it" ordering issues, and so on.

kitkat_new
0 replies
8h50m

I've never had an easier time building very complex UI functionality than with React.

How many frameworks did you have experience with?

Without a VDOM, one way data flow, and diffing

you wanted to write "or" not "and", didn't you?

darepublic
0 replies
4h32m

Without a VDOM, one way data flow, and diffing, your UI won't just be slow, it'll be full of secret O(n^2) cascades, random ifs to stop infinite cycles, random "let's update this state early so other code can immediately use it" ordering issues, and so on.

you can adhere to the same principles (one way data flow) without vdom. Not saying it's easy at large scale but it's possible. I don't appreciate people invoking fud towards anyone opting out of their tech choice.

kristiandupont
2 replies
12h41m

And from my experience building complex web UIs, those team members were right -- there are many ways to avoid the issues and using vdom is great in general. True, there are situations where it falls short, which is why you will want to fall back to other techniques for those bits of architecture. Just like your JS, Python or Ruby server will call a bunch of functions written in C or the like. That doesn't mean you should write your entire backend in C.

littlestymaar
1 replies
10h21m

Yes, there are ways to avoid the issues, and they involve abandoning the immediate mode illusion that react created in the name of simplicity.

johnfn
0 replies
3h16m

Write it in React, and if you run into performance issues, there are a bunch of well-known performance optimizations you can make which are easy to discover. It's a well-trodden path that many engineers have walked before.

Write it in your own vanilla framework, and you will effectively re-invent all the complexity of React, but in a way that no one has ever done before. It's easy at small application scales, but once your app gets large, good luck debugging the thing that exists primarily in your principal engineer's head.

k__
1 replies
6h5m

Wasn't the issue mostly solved with signals?

As far as I understand, signals make it much easier to keep the DOM updates to a minimum.

resonious
0 replies
5h55m

It sounds to me like GP got told stuff exactly like this, with the team eventually not actually doing the thing.

darepublic
1 replies
4h40m

More than once I got asked on interviews why react is faster than vanilla JS and I had to tell them no, it isn't.

jameshart
0 replies
2h3m

The clue would be in the fact that react is running in vanilla JS.

There is a persistent ‘learned helplessness’ tendency among some developers to assume that the frameworks they are using have access to magical mystical powers above and beyond those that their own code can make use of.

Framework code might well be better optimized or more tuned than the code you would write - but if you cared to employ similar techniques you could achieve those same gains; on the other hand, since by definition it’s more flexible than single-purpose code, it might not be optimal for your usecase.

johnfn
0 replies
54m

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

recursive
10 replies
19h23m

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.

Depends who "you" are. I prefer to have my DOM nodes updated in place without all the reconciliation machinery. (no implication about what you want)

spankalee
6 replies
19h9m

You don't need diffing or reconciliation to turn a description of DOM into DOM. Lit works without a VDOM.

If all JSX does is return a DocumentFragment that you then need to imperatively add event listeners to and imperatively update, how is it much better than innerHTML?

lolinder
2 replies
18h32m

1) Type safety for element props.

2) Autocomplete for element props.

3) IDE support such as refactors and jump to definition/jump to usages.

4) Proper syntax highlighting out of the box instead of the editor just saying "there's a string here".

5) A uniform pattern for defining custom components that work the same as primitives, rather than defining custom components as helper functions returning string fragments or something like that.

And so on. JSX has a lot going for it regardless of the semantics chosen. It's just a syntax that is very convenient for lots of kinds of tooling, and it's completely unopinated about the semantic context in which it is used.

_heimdall
1 replies
18h4m

These are definitely helpful, but what you are describing are all language tool features rather than features of JSX itself. 5 would be the exception, but that is just user preference of what kind of syntax one likes to write components with.

lolinder
0 replies
17h58m

Well, yes. But OP was asking about what makes this better than `innerHTML`, and the obvious answer is that support for HTML programming embedded in JavaScript strings is generally bad while support for JSX is very good across all editors.

recursive
0 replies
18h9m

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.

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

    document.body.append(app);

esprehn
0 replies
16h8m

I think the answer to that is probably "as good as soy, but with modern ergonomics". E4X was basically this and I think it's a much nicer way to build DOM trees than strings since you can't create invalid markup or concat partial tags. It also lets you reuse subtrees naturally where innerHTML makes that impossible.

Izkata
0 replies
15h58m

innerHTML loses all local state, such as which elements have focus or where the cursor is in a text field. Back when React first came out and people were getting used to the idea of VDOM diffing, they had demos front and center about how by using those diffs to only change what needed to change, such local state wouldn't be lost.

This in theory could do something to copy that local state over, or diff the two DOMs directly without a VDOM (though from the sound of it, it probably doesn't).

ericmcer
2 replies
18h36m

If you work on a team of suitable size I would hesitate to not leverage a VDom. I trust myself to not trigger dumb reflows, but the way I see a lot of people using React this could be a perf nightmare.

acdha
0 replies
18h17m

I think that hits the real problem: it’s staffing and culture, not the tool. The 90th percentile site using a vDOM is also a perf nightmare, especially for anyone who doesn’t have a recent Apple device on a fast network, and that often has big impacts on usability and accessibility as well (dynamic loading sucks on screen readers unless you put way more time into it that most people do).

I was painfully reminded of that while visiting Europe last month on a data plan which was clearly deprioritized on partner networks - the old sites with .php in the URLs loaded in a few seconds and worked perfectly, but every time something failed to load in less than 5 minutes or partially loaded but didn’t work a quick trip over to webpagetest.org showed a lot of NextJS, React, et al. scripts trickling in because clearly a form with half a dozen fields needs 25MB of JavaScript.

The root cause is obvious: you get what you measure. If businesses prioritize Jira tickets closed per day, they’re going to get this soup of things promising to be easy to use for high developer velocity and they’re never going to get around to the optimizing it. If they’re trying to be able to hire as cheaply as possible, they’re going to look for the current tool boot camps are pushing and hire based on that, not looking for deeper knowledge of web standards or experience which costs more and shrink the candidate pool. If they’re looking for a safe choice, Facebook’s marketing means all of the big consulting companies will push React and few people will pause long enough to ask whether they’re building the same kind of apps it was designed to build (long session times, tons of local state being mutated, etc.) or whether they’re willing to invest the time needed to get it to perform reliably and well.

MrJohz
0 replies
6h11m

There are more and more frameworks that avoid the VDOM but still resolve this fine. As long as DOM mutation is handled by the framework (and not done ad-hoc), and as long as the framework has a mechanism for deferring those mutations until after all reads, then there shouldn't be a problem. This is the approach taken by SolidJS, Svelte, and even the new rendering model for VueJS.

In all these frameworks, mutations happen inside effects, and effects are scheduled such that they all happen at the end of the tick, avoiding reflows and thrashing.

novocantico
2 replies
17h12m

is probably the main benefit of a VDOM

I get the "no more imperative updates" dream. I've used these frameworks for probably a decade. I've mastered them.

Me personally, I prefer imperatively updating my DOM. I get completely fine-grained control over what's happening. I can architect it to be an extremely efficient machine. I can make it extremely easy to add/change/remove/fix features in my apps without forcing myself to think according to anyone else's opinionated methodology.

nine_k
0 replies
16h52m

If you have little state, or simple uniform state, you can actually store it in the real DOM efficiently, as values of controls, or lists of similar DOM nodes under a common known parent. If most of your DOM is static, and you only need small bits of interactivity, React is an excessively heavy tool.

The farther you get into complex GUI territory, the more you want a declarative, functional approach, because it makes things simpler. The closer you are to a handful of controls with simple logic, the more you want to just imperatively tell them what to do, and leave the rest of the page alone, because it makes things simpler. We now just have better tools than jQuery for that.

newzisforsukas
0 replies
15h49m

there is no reason something imperative cannot be declarative. the war is one of style, not capability, so saying you gain "fine-grained control" is kind of meaningless, imo

dminik
1 replies
9h36m

This really just isn't true. If your state updates are at the component subtree level (like react) a vdom is a good choice. But, if you make your state changes more granular, you can get away with skipping VDOM entirely and work with just* regular dom nodes. Look at Solid or Svelte. No VDOM there, just pure granular updates.

*List reconciliation still has to happen, but you don't need to pull out an entire vdom. You just have to have some mapping between list items and their resulting DOM nodes.

SkiFire13
0 replies
6h40m

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.

austin-cheney
1 replies
17h41m

Whether you like this project or not, your comment so completely misses the point. You are confusing the JSX syntax, which is what the author wanted by extracting it away from React, for all the React candy. This is a missing the forest for the trees kind of thing.

This mind numbing reliance upon layers of abstraction nonsense around state management is why I really don't like React. State management is ridiculously simple. State management, when done correctly, is the most primitive example of MVC with no abstractions needed.

SkiFire13
0 replies
3h33m

State management is not simple. You have to constantly keep in sync two different piece of states (your data model and the UI). Making sure that when you modify some parts of your model then everything depending on that is also updated is one of the hardest things to guarantee. Fundamentally this is because it is non-local: you cannot tell what will change by just looking at the definition of what you're mutating.

You might be able to handle this while you're alone and you know everything about your codebase, but the moment you're working with someone else or in a team this will no longer be the case.

LAC-Tech
1 replies
18h27m

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.

I feel like this is one of the leakiest abstractions in all of computing. There's a reason there's an entire cottage industry around react; how to stop things rendering multiple times, refreshing needlessly, etc.

littlestymaar
0 replies
10h10m

Yeah, as much as I liked the idea of an “immediate mode API which is in fact retained under the hood which makes things both ergonomic and performant”, the reality is that React failed to deliver on that and every sufficiently big app ends up having performance problems that are then fixed by opting out of the immediate mode illusion.

JasonSage
1 replies
18h57m

I agree with the sibling comment that this really depends on the user. To take a different approach: JSX is just a different DSL to the createElement function call pattern (see Preact.h for example) and all of the benefits you’re describing come from the framework and runtime.

More concisely: JSX is just an alternate function call syntax with some useful applications.

For example at my last company we used JSX to make test data factories that had an XML-like look but were using a builder-pattern in the element creation that was able to make contextual decisions about what was in the final data. Nothing to do with React, DOM, or inability to express the same thing declaratively without JSX.

spoonfeeder006
0 replies
10h38m

For example at my last company we used JSX to make test data factories

Thats really interesting, can you elaborate more?

For example did you use a specific JSX compiler? Was that written in house or used a 3rd party library?

AltruisticGapHN
1 replies
7h17m

I'm having fun using lit-html with vanillajs, after I saw a tweet from Marc Grabanski suggesting he didn't use the full Lit library. Admittedly I am then not taking advantage of all the reactivity goodness, but I also really dislike the decorators syntax and 95% of the time I just don't need the reactivity after the first render.

It works great! I was amazed at how you can do so much in templates, it's pretty much everything I could do in Vue templates, though a little more verbose.

I built my own `VanillaComponent` class, which has a mount() method which calls the render() function which I define on the child class.

My VanillaComponent class looks like this:

    import { html, render } from "lit-html";
    
    abstract class VanillaComponent {
      abstract render(): ReturnType<typeof html>;
    
      private _mountPoint?: HTMLElement;
    
      mount(this: VanillaComponent, target: HTMLElement) {
        target.textContent = '';
        this._mountPoint = target;
        this._update();
      }
    
      _update() {
        let templateResult = this.render();
        let rootPart = render(templateResult, this._mountPoint!);
      }
    }

So I can write something like

    class MyComponent extends VanillaComponent {
      constructor(props: { label: string }) {
        this._props = props;
      }
      
      render() {
        return html`<button>${this._props.foo}</button>`;
      }
    }
Then I can instance like so:

    let myComponent = new MyComponent({ label: "I am a button" });
    let target = document.querySelector("#demo");
    myComponent.mount(target);

The base class stores the root node (target), so later I can do

    myComponent.update()
to re-render, taking advantage of lit-html's "diffing" logic.

However something I have not been able to solve with lit-html only, is when I compose parent and child components I have to do something like :

    class MyDialog extends VanillaComponent {
      render() {
        let childComponent ...  // another VanillaComponent previously instanced
        return html`
          <div>
            ${childComponent.render()}
          </div>

So the child component I need to explicitly call render() to get the TemplateResult for the parent template.

But this means I can not do `childComponent.update()` because I don't know the root element of child component, since I did not mount it explicitly myself.

I mean technically because of the lit-html optimizations, I can do `.update()` on myDialog (the parent component) after any child component's props changes, and it will only re-render what is necessary... but let's say my child component has like 1000 cards... it seems very wasteful and it would be ideal if I could re-render only the child.

I wonder if there is a trick to get around that with just lit-html?

mst
0 replies
2h48m

It's always worth checking the lit built in directives list for the one you've still missed (or at least it is for me ;).

I think in this case the ref() directive - i.e. https://lit.dev/docs/templates/directives/#ref - may be what you want. If it isn't exactly, reading how it's implemented would be my first step towards building something similar that is.

skrebbel
0 replies
2h54m

JSX in SolidJS directly returns DOM elements much like in the top part of this post, yet it does not have these disadvantages. It's true that strictly put it's not immediate mode like React and Lit are, but the framework is designed such that there's few practical downsides to that.

kolme
0 replies
3h10m

Nice, I love lit-html(1)!

I wanted to add my two pennies to the discussion. You are of correct that with that approach you lose the declarativeness but sometimes you don't need that, if the thing is mostly static.

I went this road many years ago for a project. The nice thing of this approach is getting rid of all the ugly DOM API but enjoying it's performance over innerHTML.

(1) I absolutely love lit-html but don't like the rest of the lit components framework. Luckily you can use it independently!

croes
0 replies
5h4m

Svelte and SolidJs work pretty well without VDOM

PKop
0 replies
1h13m

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.

    Calling the same method over and over again is wasteful.

    So we don't do that.

    First, we did not start with the obviously incorrect premise that the UI is a simple "pure" function of the model. Except for games, UIs are actually very stable, more stable than the model. You have chrome, viewers, tools etc. What is a (somewhat) pure mapping from the model is the data that is displayed in the UI, but not the entire UI.

    So if we don't make the incorrect assumption that UIs are unstable (pure functions of model), then we don't have to expend additional and fragile effort to re-create that necessary stability.

novocantico
26 replies
19h35m

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.

shepherdjerred
13 replies
16h52m

Just curious, have you seen Astro? I feel like it's the perfect SSG, but maybe you have some reservations that VanillaJSX solves.

novocantico
5 replies
7h5m

For all "have you tried ___" questions, the answer is the same. I've been trying all these new techs for the past 10-15 years, regularly, as soon as they come out, for the first few years with much with excitement, and later with disillusionment and less regularity.

Another user below said

We've recently moved one service from next to Astro and it was just removing a ton of boilerplate and 'dance around' code.

And I get why it happens. When you first try out a new framework, you allow yourself to learn and add its inherent complexity, knowingly and intentionally. You say to yourself, "it's part of the dream, it's going to work out; there's a vision, just trust the process." This is true with literally all frameworks.

But they never deliver. The complexity is never worth it, and in the end, the intentionally added complexity is always intentionally and gladly removed when it becomes clear that it was unnecessary complexity. This is what I am glad to have learned so thoroughly that I no longer try to learn new frameworks when I initially see its complexity, imagine adopting it in view of my experience, and recognize that its almost always not worth it.

Look at the code on vanillajsx.com. Besides JSX and types, it's plain JavaScript and DOM manipulation. Translating it to document.createElement would add almost no lines of code. There's no unnecessary complexity. That's the whole point of the site. The simplicity of discovering and removing unnecessary complexity is wonderful and refreshing, and I think a lot of people agree.

shepherdjerred
2 replies
2h42m

I think you're misunderstanding the poster you quoted. They're indicating a positive experience with Astro.

We've recently moved one service from next to Astro and it was just removing a ton of boilerplate and 'dance around' code.

---

And I get why it happens. When you first try out a new framework, you allow yourself to learn and add its inherent complexity, knowingly and intentionally. You say to yourself, "it's part of the dream, it's going to work out; there's a vision, just trust the process." This is true with literally all frameworks.

I am quite picky and have strong opinions. I've used Astro for more than a year and still love it. There is complexity (especially if you use SSR), but for the use case of "I just want a static site" it is wonderful.

Look at the code on vanillajsx.com. Besides JSX and types, it's plain JavaScript and DOM manipulation. Translating it to document.createElement would add almost no lines of code. There's no unnecessary complexity. That's the whole point of the site. The simplicity of discovering and removing unnecessary complexity is wonderful and refreshing, and I think a lot of people agree.

I don't disagree, but this doesn't replace what you might want for SSG. For one, this requires JS on the client. Astro compiles to static HTML despite using JSX-like syntax.

As an example, here's my Astro site and source code:

* https://sjer.red/

* https://github.com/shepherdjerred/sjer.red

novocantico
1 replies
2h1m

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

[6] https://github.com/sdegutis/immaculatalibrary.com

shepherdjerred
0 replies
1h18m

Thanks for taking the time to clarify & not getting hostile :)

I'll look into imlib a little more.

samtheprogram
0 replies
3h11m

So, I take it you haven't tried Astro then?

dimal
0 replies
6h5m

I like your thinking. Things have gotten far more complex than they need to be. We've been piling abstractions on top of abstractions for too long and there needs to be a culling. If we get rid something that we really did need, that's better than the alternative. It's best now to ditch it all and see what we really need.

newzisforsukas
3 replies
15h57m

astro is nearly, if not as, "complex" as react, no?

molszanski
1 replies
12h52m

As person who has been doing “react” since 2016 I would say that it removes so much of the “react” complex BS that I am surprised it is not x100 times more popular.

We’ve recently moved one service from next to Astro and it was just removing a ton of boilerplate and “dance around” code.

FractalHQ
0 replies
2h46m

That’s how I felt going from Astro to Sveltekit, but that’s a shorter distance to travel.

shepherdjerred
0 replies
12h29m

It doesn't really make sense to compare React to a static site generator.

vips7L
1 replies
15h37m

SSG?

blackeyeblitzar
0 replies
15h29m

Static site generator, apparently

_heimdall
0 replies
15h57m

I'm a big fan of Astro, though I could see it being a bit more of an adjustment for JSX users than Svelte users since Astro's syntax was originally based on Svelte.

That said, JSX can be used easily with Astro as long as you get used to at least a bit of The `.astro` syntax for wrapper components and pages/layouts.

hyperhello
4 replies
18h25m

What I’m seeing here is not new. It’s this vanilla pattern but with enough back support to leave off the framing and get the syntax highlighting:

Var button = html(’<button>im a button</button>’);

The html() function is trivial, but it just doesn’t feel like real programming to do this, even though there’s nothing else to it in the end.

hyperhello
2 replies
17h26m

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?

novocantico
1 replies
17h23m

lolinder explained it well in here

hyperhello
0 replies
17h18m

He did, and he pointed out the point is just IDE support so it has typing and autocomplete and syntax highlighting. Thanks, we agree!

mock-possum
0 replies
11h10m

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.

fredmerc
2 replies
17h27m

A JSX for Django server side would be cool.

WesleyJohnson
0 replies
16h51m

How would you envision this working?

todotask
0 replies
14h2m

Large WASM payload on your site could be optimized.

thelastinuit
0 replies
18h56m

not the hero we deserve but the villain we need

dimal
0 replies
6h1m

Love the idea. FYI, on your demo page, the todo app is 404. And you might want to spell out static site generator instead of saying "SSG" on your docs. I didn't know what SSG was, even though I've used static site generators. I had to ask the AI.

AltruisticGapHN
0 replies
7h35m

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

arjvik
17 replies
20h21m

What benefit does the virtual DOM add?

spoiler
7 replies
20h15m

DOM interactions (read, writes) are synchronous, they're very slow, and it must happen on the main thread. This can cause the browser tab to freezing if access and updates aren't carefully "curated" (ie you don't want to read-check-then-write in a tight loop; or even write too often, even if it's the same value).

It can also simplify some stuff surrounding event handling (but that's not it's main goal I think)

So people wrote various ways to defer/batch/denounce updates.

Virtual DOM is a general solution/implementation. It's not the only one, but I think you always need at least a tiny runtime to avoid too much DOM access (ie Svelte, Solid JS are fairly minimal)

meiraleal
6 replies
19h12m

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.

smallnamespace
5 replies
16h49m

How is that done without a vdom?

meiraleal
4 replies
16h24m

Lit-html uses template literals for that

https://lit.dev/docs/libraries/standalone-templates/

"lit-html lets you write HTML templates in JavaScript using template literals with embedded JavaScript expressions. lit-html identifies the static and dynamic parts of your templates so it can efficiently update just the changed portions."

smallnamespace
3 replies
13h35m

A a high level there's not much difference between template literals and JSX, they are both syntax-sugary ways to represent trees of essentially function calls.

efficiently update just the changed portions

Since actually applying each change to the real DOM is too slow, the only way to efficiently update is to batch changes and then apply the delta to the actual DOM.

That means we need to keep track of some state, namely the previously applied state and the current goal state, which you then compare.

Now, you may have noticed that we've just independently invented the concept of diffing. And the extra state that needed to be tracked can be given a spiffy name, like "virtual DOM", since it's like the DOM, but not the real thing.

So, I'm quite unconvinced by Lit-html's claim that they are able to efficiently mutate the DOM without using a vDOM anywhere.

Either their method is not efficient (for example it falls over for rapid updates), or there is a data structure under the hood that is analogous to a vDOM, even if they prefer to give that data structure a different name.

smallnamespace
1 replies
3h43m

Yes, looking it into it more, Lit-html is doing a few things to make it all work:

1. It leans on support for the <template> element from browsers to hold fragments of HTML

2. For most use cases, it has a more restricted programming model compared to React or other vdom libraries, because templates are not allowed to change the shape (the tree structure of nodes) of the templated DOM.

3. For some cases where you want it to act more like React (for example, dynamically picking an HTML tag) you must use other mechanisms such as unsafeStatic. The docs explicitly tell you this immediately triggers a re-render = slow.

So I guess that answered my own curiosity: the vDOM is mostly replaced by a collection of static templates that don't need to be diffed, because the onus is on the dev to write DOM fragments where the tree does not change.

This is a more restrictive model that what React gives you, where you can generate any fragment tree, including ones with different shapes, entirely programatically.

If you do want the tree's shape to change, then lit-html isn't promising you good performance. You'll need to use methods like unsafeStatic which are slow. All in all, this is pushing more work off onto the developer.

Is this a good tradeoff? I think for most websites you can probably work within Lit's programming model. But the benchmarks you yourself linked points to many, many vDOM libraries that are about as performant as Lit (including React, whose main downside somewhat more memory usage) and has a more convenient React-like programming model.

meiraleal
0 replies
1h13m

Thanks for the comprehensive analysis, quite interesting.

I disagree about the trade-off in convenience tho, React programming model (especially related to managing state) is quite confusing, verbose and error-prone.

Multiple useless re-renders are the norm.

useEffects inside the body of functions a very poor way to manage the lifecycle of a component and this is way simpler with Lit.

All in all, just for it to be more like React pre-15 I would choose Lit.

samwillis
3 replies
20h2m

The virtual dom makes implementing a declarative templating system easer, and declarative templates are easer for a developer to reason about, and less error prone, than having to mutate the dom directly.

People often mistakingly describe the vdom as faster than the dom, this is incorrect. It would be faster than throwing away the whole components dom and rebuilding, so the same templating code building a new dom, rather than a vdom that's then diffed. Hand crafter mutations will be faster than a vdom diff, simply because the computer is doing les work, however much more error prone.

miki123211
1 replies
19h2m

Virtual DOM is to classical JS what garbage collection is to malloc and free.

Garbage collection is less efficient, but it is sometimes very difficult to figure out exactly when a piece of memory stops being used, which leads to use-after-free, double-free and memory leak bugs.

Same goes for classical UI approaches. In classical UI, most pieces of state are kept in at least two places, once in code and at least once in the DOM.

For example, in a shopping cart, the total might appear three times, implicitly in the code (as a function that sums the prices of all the items), once as the label of the "open cart" button in the navbar, and once as text in the "your cart" modal, which that button shows or hides. THe cart may be modifiable from different places, the cart modal itself, product pages, product collection pages, order history (when re-ordering recently purchased items) etc.

In the classical approach, you need to make sure that all modifications to the cart accurately change the state in all three places. You also need to ensure that if you remove a product from the cart using the modal and you're currently on a page that lets you order the product in any way, the "remove from cart" button on that page needs to turn back into "add to cart", and there may be hundreds of different such buttons, which the cart modal needs to handle somehow. It is very easy to make mistakes here and have the state in the code (array of products) fall out of sync with what the user sees on the page.

In React, there's just one array of products, one function to calculate the total, and a lot of places in the code that use this array. WHenever the array changes, the pieces of the page that rely on it automatically re-render, while everything else stays the same. There's no way for the UI and the array to fall out of sync, and there's no need to track where the array is being used and where it's being modified.

spoiler
0 replies
19h23m

People often mistakingly describe the vdom as faster than the dom, this is incorrect.

You'll get better performance with _carefully crafted_ DOM access, but that's easier said than done, especially on a larger applications.

vDOM takes care of the "carefully crafted" part with some trade offs, especially if it also defers rendering and doesn't wccess the DOM on every update.

So yes, it's easier to write declarative UIs with it, but it's also there to address common performance issues with unchecked/eager DOM access. Even if you don't throw away the whole tree and insert a new one, it can be very slow. Just _reading_ from the DOM is slow _and_ everything stops while that's being done too.

ProofHouse
1 replies
19h39m

Slows down your app too, sometimes. Depends how well you can work with and mutate a DOM, but if all things equal no VDOM is always faster cause no diffing.

ProofHouse
0 replies
19h37m

A lot of people can benefit from offsetting mutations with rAF and dbl rAF and batching reads/writes (FastDOM), before needing or considering a VDOM. VDOM came to prominence because of REACT and then started becoming used even when it wasn't needed. It does serve a purpose and scenario when needed, tho

edflsafoiewq
0 replies
19h59m

It enabled a style of view library where you write immediate-mode type code that always recreates a whole component from scratch, versus having to write finicky code that both creates and then updates pieces of the page as state changes (dirty tracking, etc). Behind the scenes, you're creating the vDOM from scratch, which is diffed against the actual retained-mode DOM, and then only the pieces that are different are updated.

acdha
0 replies
18h33m

If you couldn’t efficiently batch updates, a vDOM could avoid repetitive updates in close succession, especially on IE6 (the browser React was designed for).

If you can control your app’s structure, it primarily adds significant increases in the RAM and CPU required for your app and slows load time because you are using a huge amount of JavaScript to emulate the carefully tuned C++ code built in to the browser. If you notice, most of the benchmarks from when React launched claiming performance wins were compared to heavyweight frameworks or complex jQuery plug-in combinations where a single user interaction might trigger cascading updates forcing the browser to rerender things which didn’t change along or to reflow multiple times in cascading update-measure-update chains. Pure DOM implementations were always faster, often by multiple orders of magnitude and once you could drop IE6, and then IE11, the DOM APIs and CSS were rich enough that much of the library code is now a net negative as well (e.g. people used to use complex code trying to build layouts which CSS grids solved).

__s
0 replies
19h19m

With vDOM I could say `x = JSX` then cache that in state, inserting it in multiple places. Switching to Solid you have to make sure to use `x = () => JSX` & there's some mental model adjustments since logic outside JSX isn't reactive

spankalee
7 replies
19h6m

E4X had the unfortunate downside of returning actual DOM instances, which needed to be updated imperatively. That's why JSX eclipsed it, and there hasn't been a serious proposal for HTML templating in JS since then.

But maybe we can revive the general idea with a modern take: https://github.com/WICG/webcomponents/issues/1069

lolinder
1 replies
18h44m

had the unfortunate downside of returning actual DOM instances, which needed to be updated imperatively.

Isn't this what we have in TFA?

bastawhiz
0 replies
1h47m

Yes, for elements. The project here also supports a notion of components, though, which E4X didn't contemplate.

olliej
0 replies
15h27m

Also E4X was only ever implemented in Firefox, never really got traction even in Firefox.

But even considering the single implementation problem, it also was just not a good language model, nor was it well specified or defined and it brought with it a pile of weird baggage and complexity.

Then because it was The Future there was no real thought into proper interop with JS (it was essentially a completely independent spec so adopted general syntax but specified in a way that meant JS could not simply adopt that syntax).

kreetx
0 replies
9h38m

With "imperatively" you mean that the user of the templating system has to do it imperatively, and that is bad? Asking because imperative updates seem to be the way to go within the implementation, instead of creating new instances of elements every time.

SkiFire13
0 replies
6h35m

which needed to be updated imperatively

VanillaJSX seems to suffer from the same problem though.

Sephr
0 replies
9h37m

E4X had the unfortunate downside of returning actual DOM instances, which needed to be updated imperatively

Firefox never shipped the optional E4X DOM APIs. I wrote a polyfill for them at the time.[1]

1. https://github.com/eligrey/e4x.js/blob/master/e4x.js

olliej
5 replies
15h34m

Fun fact, E4X is the reason JavaScript has ‘for(of)’ instead of ‘for each’ (the reason we didn’t get ‘for (:)’ is even dumber - it would conflict with ‘:type’ annotations a few TC39 members were convinced would magically be in the language)

IshKebab
2 replies
12h1m

There is a proposal to add them, though it does seem to be stalled.

olliej
1 replies
11h29m

There were proposals almost 2 decades ago. They've never gone anywhere because proponents of type specifiers don't want to do the necessary corollary: specifying the type system.

Typescript and similar can do it because they don't have to specify the type system, and can't change it in meaningful ways over time. Things in the language standard cannot be easily changed, if they can be changed at all.

IshKebab
0 replies
0m

the necessary corollary: specifying the type system.

It's clearly not strictly necessary though. Python has shown that.

I mean I agree it is pretty mad to just say "you can write types but they mean whatever" but surprisingly in practice it seems to work ok.

rlt
1 replies
14h45m

Like the type annotations that are now in TypeScript?

olliej
0 replies
11h33m

Yup, that were in typescript, pascal (and rust, etc when they came out).

But there was no real progress after years of them pushing this syntax, but failing to actually define a type system that was coherent, or a model that would allow it.

As a result I proposed `for (of)` largely to prevent sane enumeration from being blocked on the intransigence of two people.

It's also worth noting that for(:) enumeration would not even preclude their syntax - it's certainly not grammatically ambiguous - and most real world code in languages that support enumeration directly and support inference doesn't explicitly specify the types , so the ugliness of `for(let a:type:expression)` would have be rare anyway.

shrug

Given that ECMA literally killed E4X a few years later the blanket ban on "for each" or "foreach" (because it would be "confusing" in E4X) is arguably worth than for(:), but again shrug

slmjkdbtl
13 replies
17h42m

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?

sim0n
3 replies
17h19m

I would assume that lot of people just find the JSX equivalent a lot more readable and familiar (a matter of opinion, of course.)

  <div>
    <p>this is easy</p>
    {list.map((l) => <li>{l}</li>)}
  </div>
> you automatically get loops, variable interpolation etc without having to invent a compiler and new syntax

To be fair to JSX, you use regular loops, interpolation, etc without any different syntax (`{}` accepts a vanilla JS expression), you just obviously need the compiler step to de-sugar the element tags to `createElement` calls.

slmjkdbtl
2 replies
17h14m

Yeah the syntax is almost identical to vanilla js, but requiring a compiler is quite cumbersome compared to the advantage it provides imo.

presentation
0 replies
15h44m

That said if anything pretty much all of the new school frameworks and many of the tools in their ecosystems are already dependent on compilers for optimization anyway, react itself is introducing a compiler in the latest versions.

Anyway I prefer the html looking syntax if anything because it looks like the output on the page. That’s dependent on a project coding style that doesn’t unnecessarily wrap things in components, which for my company’s product I’ve set as a standard.

littlestymaar
0 replies
10h3m

Requiring a compiler also allows to catch mistakes at compile type, which is much more efficient in terms of development.

dimal
2 replies
5h42m

Here's my perspective. I never understand how some people could look at the code you pasted and think that's just as good. But different people's brains process information differently. Your example has a lot of punctuation that's very difficult for me to parse quickly. I don't see the DOM structure that's being created unless I manually pick the syntax apart in my mind, but understanding the DOM structure at a glance is far more important to me than whether I need a compiler. For the record, I'm neurodivergent. I hope that helps.

slmjkdbtl
1 replies
5h20m

Yes I can understand it helps if it looks like DOM on first sight, I'm thinking more about the functional aspect where it can achieve the same thing without requiring a compiler and a new syntax (altho you can argue it's not new syntax just js + html)

dimal
0 replies
2h20m

It doesn't achieve the same thing, though. Functionally, for the computer, it's the same. But code has two audiences: the computer and the coder who has to read and write it. And from my perspective, the human is more important than the computer. And JSX is more than a convenience at first glance. You read a lot more code than you write. If all of my code was written like this, it would add significant cognitive load, make the code more difficult to reason about and slow me down.

plonq
1 replies
17h12m

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.

usrusr
0 replies
12h9m

It also makes it easier to see what it's not: at a glance, the "p" could really be anything until you scan the context. The <p> isn't a string (that on further examination turns out to get used for marking up a paragraph), it is a paragraph demarkation (in vdom, but still).

whichdan
0 replies
14h50m

Elm works a lot like this and it's quite nice.

fredmerc
0 replies
17h24m

Keep going down that logical rabbit hole. You end up with Common Lisp!

erikpukinskis
0 replies
17h22m

You might be confusing JSX for something else. In JSX you also don’t need new syntax for loops. JSX Is JavaScript, as people like to say.

But to your point, JSX doesn’t really do much. Your h function is basically what React.creatElement does. Google “React without JSX” and you’ll see how it looks.

JSX is just syntactic sugar over React.creatElement. And that is what makes it so nice… there _are_ no special constructs for loops, or variables, or components. They are actual JavaScript loops, JavaScript variables, and JavaScript function.

It makes JSX easier to reason about than most templating languages.

65
0 replies
3h22m

Because that code is very hard to read, especially with a complex HTML structure.

Spivak
3 replies
19h40m

And then Svelte showed that you could avoid all that with a compilation step and live update the dom efficiently.

https://svelte.dev/blog/virtual-dom-is-pure-overhead

React is also at the point where re-rendering the whole app is a fiction the library maintains for you while being smarter and doing less, why not go the whole way?

ibash
1 replies
19h5m

Agree, react is way too bloated right now. The original idea and first versions were great.

culi
0 replies
18h46m

The original idea and first versions were extremely inefficient and unscalable

Aeolun
0 replies
18h26m

And then SolidJS showed that you could do the same thing even without a compilation step.

mbivert
2 replies
15h35m

(honest question, not trying to be snarky) Do you have one (many would be great) use cases where the practical gain of the virtual DOM solutions have a genuine impact?

I'm asking because, many of React (or friends) introductory material naturally focus on building things like TODO lists or Tic Tac Toe; while those offer insights into how to work with React (& cie), they're not showcasing cases where the performance gains are perceptible, and IMO not even cases where the "organizational" benefits of such libraries are salient.

eterps
1 replies
10h42m

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:

https://news.ycombinator.com/item?id=41271367

mbivert
0 replies
8h57m

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.

Definitely; it's a struggle to find precise, concrete arguments in this direction. And there are many good reasons to be conservative: e.g. inheritance-based OO was sold with "VW inherits Car"; looks great on paper, but not as much in front of real-world issues.

Apparently, that is what it ultimately boils down to:

If so, I'd be left wondering how much of this is actually caused by a lack of discipline, as seems to be for example indicated by the "dumb reflows" issues.

insane_dreamer
1 replies
17h24m

There are plenty of cases where optimizing for performance isn't necessary. This is where React is not worth the extra headache and complexity.

presentation
0 replies
15h38m

React is set to become much less complex as a user once the react compiler is in place and if you use server components/actions; in my product we’ve already basically eliminated 95% of useEffect calls, almost all data fetching, client side state management with the current gen tools, and once the compiler is in then all memoization will be gone too.

You still end up with the bloated bundle size but with one of the more modern react alternatives you can eliminate that too. So at least for me, I don’t mind the build complexity for the power I get; especially now that node itself is supporting typescript, the build side is getting simpler to set up as well.

guax
0 replies
19h30m

For me is because is hard to remember that problem while dealing the the ones react brings.

config_yml
10 replies
20h19m

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.

shove
5 replies
19h49m

I don’t recall being able to construct XML inline like this unless maybe that was a Flex server thing?

noduerme
2 replies
19h21m

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.

theturtle32
0 replies
19h8m

Nope. I worked with Flex and it's MXML files extensively. But the parent is talking about E4X, which was an extension to ECMAScript that allowed you to use XML elements inline with JavaScript in a manner VERY similar to how JSX is used today. It also included the ability to much more easily query and otherwise work with those XML document trees in native JavaScript.

config_yml
0 replies
8h14m

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 = <foo><bar>hello</bar></foo>

and then data was an object instance like you’d expect

zoogeny
0 replies
18h39m

I don't recall being able to do the XML construction inline either, but that just might be my memory.

However, the XML selector syntax was a godsend. Recursively parsing an XML tree is really a pain. E4X would allow you to do things like:

    var foo = someXml..childNodes.@attribute;
I'm not even sure if that would work actually. There were a bunch of operators for doing things like getting a collection of children that all had the same tag so you could work with XML like:

    <someXml>
       <intermediateNodeYouWantToSkip>
           <childNode attribute="1" />
           <childNode attribute="2" />
           <childNode attribute="3" />
           <unrelatedNode />
       </intermediateNodeYouWantToSkip>
    </someXml>
Another post here said people didn't want it, but I don't think that was the real reason it was dropped. There was a lot of drama at the time about Flash in general and a massive debacle about EcmaScript 4 (which ActionScript more or less adopted). There was also the whole XHTML thing happening.

Basically JSON as a format won out over XML and ES4/XHTML were ditched. Frankly, a world that revolved around XML/SOAP would have been a nightmare, so I guess killing off the easy processing of XML in JavaScript helped to stave off that potential future. XSS, XSLT and E4X were all casualties.

quink
3 replies
19h58m

https://en.wikipedia.org/wiki/ECMAScript_for_XML - Firefox had it too, but people at large just didn't want it, so it got removed. It got disabled for web pages with the release of Firefox 17, 6 months prior to the first release of React.

sltkr
1 replies
19h38m

Personally I never heard about it. So it might not be that people didn't want it, but that it wasn't promoted much.

Also, it sounds like the only browser to ever support it was Firefox? That was probably much more of a limiting factor for adoption.

kibibu
0 replies
18h41m

If you weren't coding for the flash platform you would have easily missed it.

Its a shame, E4X was really nice

mhitza
0 replies
18h43m

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.

sophiebits
9 replies
19h55m

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.

numpad
3 replies
19h24m

Maybe I'm missing something, but how would this prevent you from using setTimeout/setInterval? But I agree that these projects often work great in small use cases, but quickly crumble under "real world" scenarios.

sophiebits
0 replies
17h42m

Sure, if you blow away the entire app on every state change. But that would lose not only state defined in components (like `i` in ClickMe) but also all state implicitly stored in DOM elements (selection, focus, scroll position, input value, media playback).

_heimdall
0 replies
17h59m

I'd be hesitant to run something like a 30fps render loop in a web app. Its been years since I last saw or tried that in a real world app but it didn't end well for performance.

Your best bet would be to queue up specific UI changes that need to be made as diff's rather than checking the entire UI state. At that point, though, you might as well run them immediately as the change is needed.

If that was still a perf problem you would end up chasing a very complex solution like react fiber to partially update the UI on a loop while periodically pausing for user events.

novocantico
1 replies
17h39m

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

novocantico
0 replies
4h22m

I just added a more complex todo app to the bottom of the page. So it should give an idea of how a more complex hierarchy can respond to events elsewhere in the hierarchy and update themselves and each other accordingly.

hellojebus
1 replies
18h49m

I'm by no means an advocate of this library, and never plan to use it, but to support component props that trigger rerenders, a'la React/Vue, I would use JS Proxies here. Wouldn't be that hard to implement.

sophiebits
0 replies
17h44m

How would you suggest using Proxy?

tombl
0 replies
13h14m

Yup, in order to scale this approach to any real size (and still have confidence that everything is working together like you expect), a proper reactivity solution is needed.

For those that appreciate this approach of JSX returning concrete DOM elements, Solid works exactly like this, with the addition of a proper reactivity layer.

mg
4 replies
11h37m

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.

littlestymaar
2 replies
10h7m

With the first example you have syntax highlighting and compile-time check.

With the second of you have stringa.

mg
0 replies
9h30m

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?

moffkalast
0 replies
8h7m

The benefit is that it makes people puke from looking at it so you have more job security I guess. Putting xml onto the same line with a scripting language is like mixing toothpaste and orange juice.

I don't understand why people take such offense to calling document.createElement() or document.getElementById() or kind of document. or window. function. It's consistent and native.

cyanydeez
3 replies
18h18m

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

zazaulola
0 replies
13h15m

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.

xigoi
0 replies
9h42m

If there can be JS inside HTML, why not HTML inside JS?

1attice
0 replies
11h26m

There's a trick to it. Kind of like one of those 'magic eye' stereograms that were popular in the nineties. You sort of unfocus and boom, there it is.

It also reminds me of that Douglas Adams line about flying: it's the trick of falling and completely missing the ground, so in order to do it, you can't think about it too hard.

andruc
3 replies
4h53m

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.

andruc
2 replies
4h52m

#real-todolist has an autofocus element and I'm using Firefox

novocantico
1 replies
4h51m

Oops. Fixing now.

andruc
0 replies
4h50m

\o/

merlindru
2 replies
10h59m

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)

croes
1 replies
4h59m

Isn't SolidJS useless in the bundle size comparison?

emadda
2 replies
3h57m

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

austin-cheney
1 replies
3h40m

React came out in 2013. At that time object manipulation would likely have been, at best, only marginally faster than writing to the DOM.

First, you have to understand that at that time Firefox was about 500x faster at accessing the DOM than Chrome and about 250,000x faster accessing the DOM via the API methods than via querySelectors. Firefox and Chrome performed about equally in use of querySelectors with Chrome being a tiny bit faster. So, the DOM was already fast, but occupied a different memory space than JS.

At any rate the original motivation had nothing to do with performance. The goal was to introduce a template system that fit with React’s state/component system. JS modules weren’t a thing yet, so code organization was very different at that time and centered around concepts like AMD and Common.js, though it was mostly some form of AMD typically require.js.

The design of the template system in Vue was created to solve for the exact same conditions according to the internal organization of Vue.

emadda
0 replies
1h14m

Respectfully, my experience says otherwise:

https://jsben.ch/cSWJa

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

drikerf
2 replies
12h0m

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.

globular-toast
0 replies
11h36m

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.

edflsafoiewq
0 replies
2h15m

There are a lot of DOM util libraries that look like

  h("ul", h("li", "task 1"), h("li", "task 2"))
This is called "hyperscript-style" after an early library that used it. This is basically what JSX compiles to too. There used to be a lot of JSX vs hyperscript debates.

There's also variants like h.ul(h.li("task 1"), h.li("task 2")) using Proxies now too.

talkingtab
1 replies
5h56m

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

erikpukinskis
0 replies
2h34m

I see what you’re getting at, but technically the virtual DOM is what makes JSX declarative.

JSX doesn’t actually write anything, it’s just a templating language over React.createElement.

It’s the virtual DOM that actually syncs those structures created by createElement to the real DOM. So it’s the virtual DOM that allows you to write your code declaratively.

That’s evidenced by OP’s project, which is JSX without the declarative piece. You just get an Element and then you have to update it imperatively if you want to change anything.

ilrwbwrkhv
1 replies
15h12m

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/

xigoi
0 replies
9h46m

Many programmers seem to be scared of anything that doesn’t have semicolons and braces.

girvo
1 replies
15h59m

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

whazor
0 replies
11h35m

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

waynenilsen
0 replies
20h2m

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

spullara
0 replies
19h46m

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

recursive
0 replies
19h21m

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

nashashmi
0 replies
19h32m

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

n3storm
0 replies
8h57m

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

miika
0 replies
11h24m

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

hizanberg
0 replies
12h47m

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

frabjoused
0 replies
13h21m

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

dqh
0 replies
9h31m

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.

cies
0 replies
8h0m

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.

andruc
0 replies
4h50m

Any comparisons on performance?

andrewstuart
0 replies
17h43m

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>

NohatCoder
0 replies
13m

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

NaN1352
0 replies
8h2m

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

NaN1352
0 replies
8h5m

How does this stack up aginst using lit-html?

EugeneOZ
0 replies
13h53m

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.