because react decided to return JSX like element syntax instead of being simply a function returning a dom element doesn't mean that this should be standardized.
90s_dev · 3h ago
Given <foo bar={qux}>bla</foo>
React used to transform it into React.createElement("foo", { bar: qux }, "bla")
Now it transforms it into import _jsx from "react/jsx-runtime"; _jsx("foo", { bar: qux }, "bla")
My proposal transforms it into { [Symbol.for('jsx')]: 'foo', bar: qux, children: "bla" }
It's self-contained and generic, doesn't rely on auto-imports or globals, and doesn't have key collisions.
It's the only way I can imagine it ever being standardized.
pwdisswordfishz · 3h ago
What does <foo children={qux}>bla</foo> do?
90s_dev · 3h ago
This is a known ambiguity in JSX, to the point where TypeScript gives this error:
> 'children' are specified twice. The attribute named 'children' will be overwritten. ts(2710)
pwdisswordfishz · 3h ago
No, that's an ambiguity in React. JSX defines only syntax.
90s_dev · 2h ago
True. Then can you suggest a standardized ECMAScript JSX proposal that doesn't turn this into a "children" key on an object? Would you just transform it into an array? ["foo", { bar: qux }, "baz"] ?
WorldMaker · 1h ago
That array does better match the current function parameters, so would be a simpler proposal to existing transpilers.
90s_dev · 52m ago
Yeah it seems fine. I wouldn't be strongly opposed to it. Standardization of either is better than nothing.
90s_dev · 3h ago
Hi, I'm the guy who wrote this proposal.
tl;dr:
My proposal transforms <foo bar={qux}>bla</foo> into { [Symbol.for('jsx')]: 'foo', bar: qux, children: "bla" }
It's self-contained, generic, doesn't rely on imports or globals, and avoids tag key collisions. It's the only way I can imagine it ever being standardized.
I think it is still useful as a function call syntax. There's a variety of data structures that people convert JSX to already, including direct-to-DOM. Choosing a blessed one seems harder than a function calling convention, and a function calling convention resembles other language things like tagged templates.
I agree that the current "auto-imports" to find that function are nonsense and far too React specific. But the current "global" approach isn't actually "require a global" it is "requires a variable in scope" so it works with "Bring-Your-Own-Import" just fine. We just need a better standard for what that BYOI function is called by default. `React.createElement` is obviously silly. I've been happy in my own projects standardizing on `jsx` as the function name. (It resembles the auto-imports, too, even if I still don't understand why React thought it needed an extra underscore.)
I think the biggest tweak that would be nice if we are also wishing for ponies would be a way to set that function per block of JSX like the way that you can tag a template. That would make it far easier than the current per-file or per-project configurations.
jsx<foo bar={qux}><zoot>bla</zoot></foo>
That doesn't look terrible. Not great either. But I'm sure the big problem with it is that it makes the `jsx < foo` less than versus `jsx<foo />` tag parsing a lot harder.
ETA: New idea, what if it was a fake dot tag .< operator?
if (condition) {
// new scope
const jsx = // ...
return <foo/>
}
In practice, "variable in scope" is essentially the same as "global in scope" and React used to require you to import 'react' at the top of every JSX file for this reason. Your solution is tantamount to just changing that to import jsx from 'react' but still requiring it explicitly.
The main problem with this is that it's non-trivial to make sure variables are in scope before/while loading a file, and they should be loaded on an as-needed basis instead of always, which is what inevitably ends up happening when you need React classic, you import React at the top of every HTML file. These problems seem to be what led to auto-imports.
I admit that my solution still does require you do something like import interpretJsx from 'something', and even further it requires you to manually call interpretJsx(<foo/>) which can be tedious and verbose.
The main benefit is that at least it becomes a standard. And besides, we probably won't need to call those functions everywhere, just at the top of a tree, like the same place you call React.render(root)
The main downside is lack of monomorphism, especially if you're passing an entire tree to interpretJsx(). I admit this is not solved by my proposal. But I still wanted to put the proposal out before smarter eyes than mine anyway.
> *It's NOT a proposal to incorporate JSX into the ECMAScript spec itself.* It's intended to be used by various preprocessors (transpilers) to transform these tokens into standard ECMAScript.
(Emphasis theirs.)
palmfacehn · 6h ago
I'm happy with <template> elements. No shade on JSX, but I prefer the abstraction of HTML, CSS and vanilla JS.
WorldMaker · 37m ago
Template elements don't have great Typescript type checking today. I've been very happy with both, writing things in TSX with deep type checking and then statically rendering them to Template tags.
I consider the slots-based approach clunky and prefer implementing my own templating system with simple functions returning HTML in template strings.
palmfacehn · 4h ago
I did this in the past, but I found that templates simplified the process. The same functions I use to populate/update elements can be reused on existing elements and newly cloned template elements. It can be done in JS by returning strings of unpopulated elements, but then my display is further mixed with logic.
I like to create the HTML and CSS as I'd like it with test data, then just wrap that with <template> tags. Easy to preview without triggering function calls or pasting it into code.
Probably not important, but as I recall I think there was some minor overhead in translating from a JS String to an Element.
Can you recommend examples and tutorials of happy <template> usage, showing advantages over building values for innerHTML etc. as text from strings and template string literals?
This MDN page is where I first discovered the <template> element, and I wasn't very impressed: verbosely operating on one textContent at a time, using ordinal indices into very untyped querySelectorAll results, apparently gratuitous complications with document fragments and shadow DOM.
90s_dev · 3h ago
I made this proposal because JSX is more generally useful than generating HTML. You can use it for configuration, or views for other GUIs like I do in https://90s.dev/os/ or to describe basically any kind of tree.
React used to transform it into React.createElement("foo", { bar: qux }, "bla")
Now it transforms it into import _jsx from "react/jsx-runtime"; _jsx("foo", { bar: qux }, "bla")
My proposal transforms it into { [Symbol.for('jsx')]: 'foo', bar: qux, children: "bla" }
It's self-contained and generic, doesn't rely on auto-imports or globals, and doesn't have key collisions.
It's the only way I can imagine it ever being standardized.
> 'children' are specified twice. The attribute named 'children' will be overwritten. ts(2710)
tl;dr:
My proposal transforms <foo bar={qux}>bla</foo> into { [Symbol.for('jsx')]: 'foo', bar: qux, children: "bla" }
It's self-contained, generic, doesn't rely on imports or globals, and avoids tag key collisions. It's the only way I can imagine it ever being standardized.
I currently use JSX for:
* Creating custom GUI view objects in https://90s.dev/os/
* Using JSX as a convenient & composable string-builder in Node.js via https://immaculata.dev/ when generating all my sites at build-time e.g. https://github.com/sdegutis/immaculata.dev/blob/main/site/te...
* Using JSX to generate plain DOM objects in the browser in some of my sites like https://github.com/sdegutis/minigamemaker.com/blob/main/site...
I agree that the current "auto-imports" to find that function are nonsense and far too React specific. But the current "global" approach isn't actually "require a global" it is "requires a variable in scope" so it works with "Bring-Your-Own-Import" just fine. We just need a better standard for what that BYOI function is called by default. `React.createElement` is obviously silly. I've been happy in my own projects standardizing on `jsx` as the function name. (It resembles the auto-imports, too, even if I still don't understand why React thought it needed an extra underscore.)
I think the biggest tweak that would be nice if we are also wishing for ponies would be a way to set that function per block of JSX like the way that you can tag a template. That would make it far easier than the current per-file or per-project configurations.
That doesn't look terrible. Not great either. But I'm sure the big problem with it is that it makes the `jsx < foo` less than versus `jsx<foo />` tag parsing a lot harder.ETA: New idea, what if it was a fake dot tag .< operator?
Maybe?The main problem with this is that it's non-trivial to make sure variables are in scope before/while loading a file, and they should be loaded on an as-needed basis instead of always, which is what inevitably ends up happening when you need React classic, you import React at the top of every HTML file. These problems seem to be what led to auto-imports.
I admit that my solution still does require you do something like import interpretJsx from 'something', and even further it requires you to manually call interpretJsx(<foo/>) which can be tedious and verbose.
The main benefit is that at least it becomes a standard. And besides, we probably won't need to call those functions everywhere, just at the top of a tree, like the same place you call React.render(root)
The main downside is lack of monomorphism, especially if you're passing an entire tree to interpretJsx(). I admit this is not solved by my proposal. But I still wanted to put the proposal out before smarter eyes than mine anyway.
https://facebook.github.io/jsx/ is a mirage, apparently.
(Emphasis theirs.)
[0] https://worldmaker.net/butterfloat/#/stamps
I like to create the HTML and CSS as I'd like it with test data, then just wrap that with <template> tags. Easy to preview without triggering function calls or pasting it into code.
Probably not important, but as I recall I think there was some minor overhead in translating from a JS String to an Element.
I wish this were available natively.