Preferences

The direct semantics of JSX are "transform this syntax into this nested sequence of function calls and this layout of arguments". That's been the case since nearly the beginning. The only real semantics "fights"/"changes"/"React-specifics" you can see in the compiler options in Babel and Typescript: what the function is named and how do you import it. Enough other libraries that aren't React use JSX that it is easy to see what the generic approach looks and find ideas for runtime configuration of "jsx function name" and an import strategy that isn't just "import these hardcoded names from these hardcoded React modules".

> The direct semantics of JSX are "transform this syntax into this nested sequence of function calls and this layout of arguments".

Not exclusively. SolidJS, for example, transforms the syntax into string templates with holes in them. The "each element is a function call" approach works really well if those calls are cheap (i.e. with a VDOM), but if you're generating DOM nodes, you typically want to group all your calls together and pass the result to the browser as a string and let it figure out how to parse it.

For example, if you've got some JSX like:

    <div>
      <div>
        <span>{text}</span>
      <div>
    <div>
You don't want that to become nested calls to some wrapper around 'document.createElement`, because that's slow. What you want is to instead do something like

    const template = parseHtml(`
      <div>
        <div>
          <span></span>
        <div>
      <div>
    `);
    template.children[0].children[0].innerText = text
This lets the browser do more of the hard parsing and DOM-construction work in native code, and makes everything a lot more efficient. And it isn't possible if JSX is defined to only have the semantics that it has in React.
> You don't want that to become nested calls to some wrapper around 'document.createElement`, because that's slow.

It's really not slow. It might seems slow if you're using react behavior which re-invokes the "render function" any time anything changes. But eventually they get reconciled into the DOM which creates the elements anyway. And most other code bases are not based on this reconciliation concept. So I don't think that's a given.

It's significantly slower than letting the browser do the work for you. Obviously performance isn't the only concern, but (a) as a user, I don't want people wasting my CPU cycles unnecessarily, and (b) I've worked on applications where ever single millisecond counted, and the createElement approach would have made a material difference to the performance of the application overall.

Also, there's no reconciliation happening here. In SolidJS, as well as in Vue in the new Vapor mode, and Svelte, the element that is returned from a block of JSX (or a template in Svelte) is the DOM element that you work with. That's why you don't need to keep rerendering these components - there's no diffing or reconciliation happening, instead changes to data are directly translated into updates to a given DOM node.

But even if you don't need to worry about subsequent re-renders like with VDOM-based frameworks, you still need to worry about that initial render. And that goes a lot quicker if you can treat a JSX block as a holistic unit rather than as individual function calls.

`document.createElement` performance has come a long way in the last few years. The HTML "string" parser is incredibly well optimized in every browser, but the performance difference between smash a string into `innerHtml` and `document.createElement` approaches has shrunk a lot, especially in the time since React started doing so much VDOM work to avoid both tools as much as possible.

The difference shrinks even further with `<template>`/HtmlTemplateElement, its secondary/content `document`s for `document.createElement` and `document.importNode` being faster for cloning+adoption of a `template.contents` into the main document than string parsing.

I've got work-in-progress branch in a library of mine using JSX to build HtmlTemplateElements directly with `document.createElement` and right now `document.createElement` is the least of my performance concerns and there is no reason to build strings instead of elements.

(ETA: There are of course reasons to serialize elements to strings for SSR, but that's handy enough to do with a DOM emulator like JSDOM rather than need both an elements path and a string path.)

We're talking about the behavior of standardized JSX. Different frameworks have different approaches. This supports the only point I'm trying to make here. Which is that there's no expected standard behavior of JSX on which tostandardize.
No. You're thinking of how React uses it.

The library [0] I wrote that uses JSX converts expression attributes into parameter-less lambdas before providing them as function parameters or object properties. This is a different behavior than react's build tools or any of typescripts jsx options. But it's not inconsistent with the spec.

[0] https://mutraction.dev/

More libraries than React work just fine with the existing Babel and/or Typescript JSX options. snabbdom is a big one to mind that isn't React/Preact, but there are plenty more.

The space that the Babel/Typescript JSX options describe is a constructive space for more than just React.

This item has no comments currently.

Keyboard Shortcuts

Story Lists

j
Next story
k
Previous story
Shift+j
Last story
Shift+k
First story
o Enter
Go to story URL
c
Go to comments
u
Go to author

Navigation

Shift+t
Go to top stories
Shift+n
Go to new stories
Shift+b
Go to best stories
Shift+a
Go to Ask HN
Shift+s
Go to Show HN

Miscellaneous

?
Show this modal