-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
How could JSX work natively? #5
Comments
What I don't like about existing JSX implementations is that they require a bundler. What I don't like about imlib's current JSX implementation is that it requires a runtime (since it's not bundled). Ideally, JSX would compile to something native: Maybe JSON trees?<a href="foo">bar</a> === { a: [ { href: "foo" }, "bar"] } I like this because it's completely agnostic, and leaves parsing to the user. I don't like this because it's not as convenient: div.replaceChildren(<b>test</b>) // no longer possible
div.replaceChildren(toDom(<b>test</b>)) // have to transform it with a a helper now Maybe well-known class preexisting at runtime?<a href="foo">bar</a> === new JSX({ a: [ { href: "foo" }, "bar"] })
// or just
<a href="foo">bar</a> === new JSX(a, { href: "foo" }, ["bar"]) This has the same flaws as If JSX is ever made native, this wouldn't be an issue. A But if |
I don't know if this is the place but;
|
@haggen I get where you're coming from, because typically in React-style JSX, we expect everything to be JSX syntax. In my examples for vanillajsx.com, I tried to write it in the simplest possible way, getting rid of the assumption that everything needs to look like React. In this case, a TodoList object simply has a |
@sdegutis Makes sense. |
@haggen I think it makes sense for DOM methods, like |
The challenge of Vanilla JSX is that there's no runtime. One has to be looked up or injected at compiletime or JS runtime. Even in the proposal above, I think this is what everyone is stuck on in terms of even coming up with a way JSX could become native. |
This is why I like the JSON-tree solution above: <a href="foo">bar</a> === { a: [ { href: "foo" }, "bar"] } Then frameworks are all free to do whatever they want with this tree. The difficulty for vanilla jsx is that it's not as convenient. You can't just add that into a DOM without converting the tree first. The difficulty for React is (as far as I remember from 2014) that it's slow. I don't know if this is still true today though. |
I'm just going to leave it here: |
@phaux thanks, it's a clever idea, and more use-cases that show what vanilla JSX could be like, and what baseline it would need to satisfy. |
I had the idea yesterday to embed the JSX tag, attributes, and children, all into the same object literal: type JSX<Tag = any, Attrs extends { [attr: string]: any }> = { jsx: Tag, children: any } & Attrs;
<a href="foo">bar</a> as { jsx: "a", href: "foo", children: ["bar"] }
<Foo x='1' y='0' /> as { jsx: Foo, x: '1', y: '0' }
<hr/> as { jsx: "hr" } I think it's clean, semantic, and twice as memory-efficient as the previous idea. The downsides are:
|
I wonder... What if there becomes a built-in JSX class (shimmed at first), which is entirely configurable by libraries? The first way I can think of is that libraries could set its base class (prototype) at runtime. Two problems with this:
|
The plain JS object syntax is available for testing in 3.0.0-beta1 |
Hey there, just skipped through. Love to see other people falling in love with jsx and plain DOM. I am working on a maybe-similar thing. https://github.com/nhh/zero You might want to check out my jsx dom type mappings. https://github.com/nhh/zero/blob/main/zero.ts I already started building stuff with it. https://www.0xfff.de/zero/index.html Its nowhere near finished or polished, I just started exploring TSX last week. (Inspired by vanillajsx.com) |
I just published a proposal to standardize JSX. Would appreciate any feedback. |
Let me start by saying that this is so incredibly simple, that it makes me want to cry. As for feedback: The fact that For example: const b4 = <web-component children="xyz">{"Hello"}</web-component> compiles to: const b4 = {
[Symbol.for("jsx")]: "web-component",
children: "xyz",
children: ["Hello"]
}; Suppose that Rather than use a {tagname: Button, attributes: { children: "xyz" }, children: ["Hello"] } It's concise, unambiguous, and very readable. I understand why one would want to have the attributes at the highest level to ease access, but at the same time, I think most JSX literal value usage will be very "meta" by its nature. I.e. read and managed by library code, not directly by user code, and so I don't think having the attributes mixed into the top level object will actually improve the experience. |
If you want to "know" that something was a JSX, then I would have a {} instanceof Object //=> true
[] instanceof Array //=> true
/xzy/ instanceof RegExp //=> true
<div/> instanceof JSX //=> true |
@cowboyd I like that idea, it seems clean, fits the pattern, and is very easy to implement when using the classic react transformation ( |
@cowboyd Although I like the consistency of that method, I'm not sure it would be good on the path to standardization, namely because of the need of a class that would need to either be imported or become a global. I feel like those two options would impede standardization, whereas the same semantic check is possible with the symbol solution that I proposed above. Plus, the class would literally be functionality-less, as I can't imagine any common functionality for a JSX expression between all frameworks, except for the semantic check itself. |
I've updated my JSX standardization proposal to fix efficiency issues brought up elsewhere. At this point I'm convinced this is the only form of JSX transformation that has a chance to be standardized into ECMAScript, because it's self-contained, simple, and roughly as efficient. For those reasons, I'm going to move forward with this implementation in all my projects. But I also doubt the JS community has any interest in moving JSX forward in any direction other than what's dictated by a few primary entities, and I have no ability to spread interest in this proposal. So I'm done trying to advocate for it. |
I feel the opposite is true. This would be a novel use of Symbol which in all other instances is used to represent an interface or a protocol between an object and the javascript runtime, not a marker for developers. If anything, a But this is a quibble, and one that can ultimately be adjudicated by feedback on a proposal. However, I think that the basic technique is so radically simple, and solves so many problems that I think it actually is possible that it gains traction and makes it through the standardization process. It would no doubt be a lot of work, but I think the biggest objections would come around two things: 1) will it work with React, and how will it perform in comparison. If the answer is "well", and "well", then it would make any argument against this proposal pretty thin. Maybe there is a good reason why this isn't a good idea, but I would love to find out why. |
@cowboyd (a) that's just the typical use of Symbols; inherently they have no purpose aside from being unique strings, and that solution fits well here; (b) for it to gain traction, it needs to be heard, and I can't effectively get anyone besides you (and one preact dev in deno who seems strongly biased against it) to hear my proposal; (c) I highly doubt it could ever be as fast/efficient as rendering JSX as a function call that's literally designed/tailored for React. |
You are heared. Just dont give up so early. You got this 👍 |
Even though it literally has no chance to be as efficient/fast as the status quo, everyone knows that React is already slow, and if people see the advantage of JSX being syntactic sugar for plain objects, they might be okay with the slight performance loss. But React is the king of JSX mindshare, and everyone loves it, and I highly doubt anyone will care about what JSX can do outside of it; except other framework authors, who are the only ones capable of pushing this proposal. And I have been slowly creating utility libraries around vanilla DOM to wrap common patterns, which I guess could gain traction if I push further into that and clean it up, especially if it shows the true convenience of vanilla JSX. The last todolist example on https://vanillajsx.com/ has a lot of low hanging fruit for extraction to a lib, esp. around clean handling of events in DOM trees respecting node-moving. |
I disagree. I am already working on a helm alternative in jsx which is a wildly different usecase. https://github.com/nhh/k8x People will get it too JSX is awesome and does not only belong to react or the browser. |
@nhh that's actually pretty interesting, but why not just use JSON? That's already standard for config use-cases like this, no? |
Compare json to jsx and you got your answer 😁 Edit: I worked with helm for several month now. Our charts are full of variables, helpers, hard to read and im general extremely hard to configure and reuse. k8x aims to solve that. |
I could see updating https://vanillajsx.com/ to add a comprehensive explanation, use-case examples, a full Q&A, and make it like a full page thorough argument in favor of vanilla JSX, if others were willing to post it to various forums to see if it can gain traction. Other than that, I'm out of steam for this cause. My only task left is publishing @imlib/babel-transform-vanillajsx-plugin. |
Maybe its ok to get some distance to the whole standardization thing. Just experimenting with what we already have and appreciate where it can go. It does not need to be standardized to be a good idea. Bundler wont go anywhere in the next decade, so a transpilation step is also not that big of a deal. |
@nhh my goal is to make writing immaculatalibrary.com as smooth and easy as possible, and that's where my vanilla JSX came from; types are coming to vanilla JS, the only major thing left is JSX, and I would love to get rid of a transpiler entirely. That's been my entire motivation for vanillajsx.com and this proposal for standardization. But in retrospect, yeah, it would be removing only about 20 lines of code from imlib, and only when both types and JSX become native. So good point, I agree. (Ideally, I don't want to have to maintain any lib at all, I want everything to be native; but imlib has several other efficiency/performance features I can't find or easily build on top of anything else right now unfortunately; without it, maintaining immaculatalibrary.com would be orders of magnitude slower.) |
What got me interested in this in the first place is my work with MDX which is a massive use-case with a large community. And there, the ability to not be able to transform JSX in different ways within the same file is a real millstone. But in any case, thanks for the time you put into this. |
Just an idea: Write custom loader/plugins for vite and return different jsx factory invocations. import {Chart} from './snowfall.js?customLoader'
export const year = 2023
# Last year’s snowfall
In {year}, the snowfall was above average.
It was followed by a warm spring which caused
flood conditions in many of the nearby rivers.
<Chart year={year} color="#fcb32c" /> |
Then the Ugh, the JS ecosystem is so fragmented, disorganized, ad hoc and messy. I sometimes feel like Sauron and just want to swoop down and take control and fix everything for everyone. But the countless use-cases are so complex that I know none of us probably can fix this mess, individually or collectively. |
Getting back to the original question, maybe it would be useful to layout what is needed to have a proper implementation roadmap. Something like
With that we can get a somewhat realistic feeling for the impact that this standardization has, and maybe better understand why everybody is just fine with using a preprocessor/buildtool for this. Personally, I think relying on a preprocessor like tsc, esbuild or babel is fine, because using typescript will always need a build chain and it already supports typing and jsx-factory configuring. What do you think? |
@nhh I've settled on this JSX transformation as the most reasonable path to standardization. The babel plugin source is linked on that page. Runtime authors said they won't adopt it until framework authors adopt it en masse. Framework authors probably won't adopt it because it's inherently (slightly) less efficient and less ✨magical✨ than the status quo: // now
<a href='foo'>bar</a>
// transforms to
import _jsx_ from 'react/jsx-runtime';
_jsx('a', {href:'foo'}, 'bar')
// with this proposal
React.createElement(<a href='foo'>bar</a>)
// transforms to
React.createElement({ [Symbol.jsx]:true, tag:'a', attrs:{href:'foo', children:'bar'} }) I promise you nobody will prefer the second except people like me who can't stand React who are in the vast minority. Then again, React 19 has new |
Thx for inspiring me with vanillajsx! Since reading about it, I habe a ton of fun abusing jsx syntax to my will 😄😁😁 |
Also I do see opportunities for the desugared object to be smaller or more efficient or whatever. For example // given
const b1 = <a href='/foo'>Click me</a>;
// instead of
const b1 = {
[Symbol.for("jsx")]: true,
tag: "a",
attrs: {
href: "/foo",
children: "Click me"
}
};
// it could become this which mimics jsx() parameters
const b1 = {
[Symbol.for("jsx")]: ["a", {
href: "/foo",
children: "Click me"
}],
};
// or this which mimics React.createElement() parameters
const b1 = {
[Symbol.for("jsx")]: [
"a",
{
href: "/foo"
},
"Click me"
],
}; But (a) anyone who has sway in standardizing it would probably prefer the more explicit form, and (b) people like this guy would probably consider it less compatible with JS engine optimizations. |
One goal of this project has been to experiment with what JSX could possibly look like in the future if it ever became native syntax. What would the formal spec say about its implementation? How could it be as isolated and decoupled from every framework as possible, while still being convenient?
I'm opening this issue for discussion/brainstorming on this topic. We could use experimental versions of imlib as a testing ground for it.
The text was updated successfully, but these errors were encountered: