6 ms·
JavaScript Is Enough
- leemcalilly 6mo ago[flagged]
- Brysonbw 6mo ago[flagged]
- koakuma-chan 6mo agoNo it's not. Stop upvoting AI slop.
- arbayi 6mo agonot the author of the gea but from what I can see in the readme, the ideas go back to 2017, erste.js and regie were earlier versions of the same concept. https://github.com/dashersw/erste https://github.com/dashersw/erste https://github.com/dashersw/regie https://github.com/dashersw/regie
- aappleby 6mo agoYou wrote and shipped this in three days, eh?
- arbayi 6mo agonot the author of the gea but from what I can see in the readme, the ideas go back to 2017, erste.js and regie were earlier versions of the same concept. https://github.com/dashersw/erste https://github.com/dashersw/erste https://github.com/dashersw/regie https://github.com/dashersw/regie
- tredre3 6mo agoIt was likely almost entirely AI-generated but there are two oddities: - MIT — Copyright (c) 2017-present Armagan Amcalar: It would be an interesting bout of hubris to give yourself a copyright that predates the beginning of the project by 9 years. - The README lists sizes as "kb" rather than "KB": I find it odd that it would get units wrong unless it was specifically instructed to do so?
- marssaxman 6mo agoThat is a remarkably pedantic nitpick. If you're going that far, you should really be complaining that it's not "KiB".
- beardyw 6mo agoI don't think it's pedantry, it's looking for clues as to the age of this.
- jimmywetnips 6mo agonah, it's pedantic
- dashersw 6mo agoHi, the author here. Gea is built upon erste and regie that I released in 2017 and 2019. Hence the copyright. I thought a lot about this, also thought of releasing it as a new version of erste, but it would be too big of a change and re-imagination, and I didn't want to introduce the dichotomy and stress introduced by Angular 2.
- dashersw 6mo agoHi, the author of Gea here. Gea is built upon the principles introduced by 11 years of history, based on all of the UI frameworks I have authored so far, the most recent of which are erste and regie from 2017-2019. And Gea itself is written over 6 months, the history of which is compressed in the initial commit as I don't believe in populating Github history with crappy commits that alter design decisions and introduce breaking changes. You can imagine Gea was closed source before the 1.0.0 launch, and made only open at the end.
- tkzed49 6mo ago> Solid has signals and createEffect... Gea takes a different path. It introduces no new concepts at all. proceeds to introduce Stores and Components what makes this magically easier than Solid, or any other Proxy-based reactive store frameworks?
- dashersw 6mo agoStores and Components are basic classes that don't introduce any new concepts (other than the fact that the JSX goes into the template method of the Component, and that they are reactive behind the scenes). There are no hooks like useState, and the design philosophy is that everything should feel as native and natural as JavaScript.
- tkzed49 6mo agoyou continue to contradict yourself by introducing concepts and saying they are not concepts. I get what you're trying to say, that React hooks have special semantics, and that your abstraction feels more "native". again, not sure how this is more "native" than Solid signals, just as an example.
- dashersw 6mo agoYou are bringing up an important topic. The way I see it is that Gea's Store is a plain old JS class. It's just a native class. There really is no special syntax you need to pay attention to. Whereas Solid signals require you to follow a specific syntax and approach, and has its own gotchas. Like, the language doesn't have a createSignal method by default, and you don't "execute" what look like values in JS as you need to do in Solid, and although I'm looking forward to the official Signal API, Solid isn't following that either. That's basically how Gea is more native, because stores are plain classes. I hope this clarifies my point a little bit more.
- slopinthebag 6mo agoWhat is the difference between mobx or solid stores or any of the reactive frameworks that do reactivity on proxy objects?
- deleted 6mo ago[deleted]
- afavour 6mo ago> The Vite plugin analyzes your JSX at build time, figures out which DOM nodes depend on which state, and wires up surgical patches — invisibly. This part interests me… if it’s able to be brought to React somehow. Too many sites are shipping entirely reactive DOMs where only a tiny minority of content actually changes. The fact that the entire project appears to have been written in three days, however, gives me some deep doubts.
- dashersw 6mo agoI wish I could bring it to React! That would save so many developers and so much natural resources! I've been working on the library for 6 months, and it's built upon my previous libraries tartJS (2011), erste (2017) and regie (2019). I just like to squash my commits before I make a public release, and that just happened 4 days ago :)
- PufPufPuf 6mo ago[dead]
- cattown 6mo agoTwo-way props! Yikes! That was a mess in the first version of Angular. I thought the consensus was that two-way props binding just opened the door to difficult to understand side-effect laden code.
- jinushaun 6mo agoTwo way props make for a nice demo, but are a nightmare to maintain.
- kikimora 6mo agoWhy? Instead of creating an event handler that changes a property you declare same thing as binding. Why this creates more issues than manual event handlers?
- dashersw 6mo agoThe bindings in Gea work just like in JavaScript. Two-way if an object is passed, one-way if a primitive is passed. I think it's best to stick to the idioms of the underlying language.
- anamexis 6mo agoI don’t know if I’d call it an idiom — rather I’d argue that in modern JavaScript, mutating passed objects is often an anti-pattern.
- dashersw 6mo agoI personally agree. But the default expectation (and therefore the design) should follow the practices of the language. If JS allows mutations on the objects passed to a function to be reflected on the parent, I believe frameworks should follow this paradigm. And in the end in Gea developers have full control over this, just in the same way they do in real life. `child({ ...obj })` easily solves this, for example, in both idiomatic JS and in Gea.
- 6mo ago
- nine_k 6mo agoDisclaimer: I only read description, did not try to code. What I like: the smart compiler that determines the actual dependencies, no need to declare them. Apparently the compiler is so smart as to compute the DOM diffs at compile time, which eliminates the need for virtual DOM. What kills it for me: the two-way binding. The binding should be one-way to preserve your sanity as the project grows. Two-way bindings allow to build highly reactive Ruby Goldberg machines where anything can trigger anything else, and you won't know, because it's just a mutation of a property somewhere, indistinguishable from a non-reactive mutation. Two-way bindings are callback hell squared. I want one-way data binding, immutability, and basically FRP. The biggest demonstration of FRP's immense real-life success is not React. It's the spreadsheet. This may be good for small pieces of interactivity. But I likely would go for HTMX for that.
- dashersw 6mo agoHi, the author of Gea here. I have a long history of thinking about one- and two-way bindings, and I believe JavaScript as the language has a great solution to this. If you pass an object to a function, it's two-way bound, and if you pass a primitive, it's one-way bound. So I built Gea to replicate this. If users choose to pass an object in a one-way bound scenario, they could create a new object and pass that in, and it would work. By the way, just as a syntactic sugar, Gea supports function components, too.
- nine_k 6mo agoThank you for your project, it's elegant! It's pretty obvious that one-way binding is a two-way binding with one of the roads not taken. I see that e.g. a careful naming scheme could make it obvious what is reactive, and what is not. OTOH React arrived where it's now not by allowing a particular approach, but by enforcing it.
- linhns 6mo agoNot a JS dev but the site need to move away from tinted retro look if you want to stress the modernness.
- dashersw 6mo agoThank you! It's the author here. I thought hard about this, and one angle Gea is bringing is that the old is new again, that's why I harkened to a retro style. I also just plain love synthwave, so... :) but you are right, and as Gea matures I believe we will iterate on the homepage.
- s-y 6mo agoBro, is there anything you haven't thought hard about? I read throughout the thread and the answers sound really LLM-y. Although these days we might be calling wolf about gusts of wind. Great framework tho. Awesome job.
- dashersw 6mo agoHeh, sorry it comes across as LLM-y, honest and human-written answers here only. I'm tired of AI slop as much as the next guy, but, yes, I _really_ took my time with Gea. I've been working on writing JS frameworks for 15 years, this is actually the 3rd generation of similar ideas, following tartJS (2011) and erste & regie (2017-2019). It took me several years to solidify what I expect from this generation, and I've been working on Gea for the past six months. That's why I thought about and evaluated many aspects raised here. And thank you!
- wetpaws 6mo ago[dead]
- ch_sm 6mo agohey! Great job on this, dashersw. I‘ve believed for a while now that the compile time dependency analyzer approach is the only good way for frameworks like this. Really neat choices on the API surface as well – so simple! Launching with a headless UI lib is smart. will try both in a side project soon! thanks and cheers!
- dashersw 6mo agoThank you! Honestly I tried building a replica of shadcn but that didn't prove to be viable. I thought it would be very difficult for developers to adopt without a first-party UI library so built one on top of Zag.js. And I expect a lot of AI-assisted coding sessions to happen with Gea so I also baked in enough AI skills to enable assistants, as well as migration guides for React. Hopefully these will help onboarding early adopters. Please let us know when you give Gea a try!
- skyturkish 6mo agoThere are many ways to write JavaScript, for example by destructuring variables, etc. How do Gea proxies work to retain reactivity then?
- dashersw 6mo agoThis is one reason Gea has lots of examples in the repository. It's an area of active development—I'm working on adding compiler support for more and more patterns. Basically, since we can analyze the code statically, and follow the dependencies even if they are destructured, we can create proxies or special handlers for each situation. It's a tiring job and unfortunately there's no one global, easy solution, especially as you pointed out, some of these patterns don't work in proxies. But I believe we cover an important base of idiomatic JavaScript right now, and I'm continuing with new releases to improve the compiler's handling on more exotic ways to write JavaScript. Of course, one down-side of the compiler approach is, for example, if there's a statement you want to make reactive whose signature is only resolved in runtime (like a computed property name) it's practically impossible to wire. But Gea exposes enough of the underlying component structure so it's kind of straightforward for a developer to manually write an observer for these cases, and do the updates to static properties (that are rendered) in runtime. Hope this clarifies the approach.
- puskuruk 6mo agoHow does this thing run so close to VanillaJS without carrying any extra baggage along for the ride?
- dashersw 6mo agoI personally don't believe anything can beat hand-built and fine-tuned vanilla JS code, and in the benchmarks we see Gea beating vanilla JS implementation (for example in partial update or swap rows). One reason for this is Gea's compiler has special cases like row swap's to be as minimal calculations and DOM operations as possible. The compiler, also, is evolving to recognize more and more patterns, and compile them into a miniscule overhead. One thing I borrowed from my earlier library erste is event delegation. Instead of creating event handlers bound to each DOM element (say, in a list render) which is memory-heavy and also consumes a lot of CPU cycles, Gea simply attaches one event listener per type on the body and uses a `.matches()` call to check whether that event applies to a given DOM element. This is one of the main reasons why Gea is so performant—there's no excess/unnecessary memory allocation or CPU cycles. This is also reflected in the benchmark results.
- puskuruk 6mo agoThank you for your detailed answer!
- yeargun 6mo agoGJ one your library One random recommendation I could give based on my experimentations on both firefox and chromium you can attach symbol based custom properties to each such dom node 'class' and apart from using .matches(), symbol attribute check. this outperforms class based equality checks by a small margin. very marginal but hey, you are clearly tryharding.
- dashersw 6mo agoThank you! Very interesting insight—I'll have a look.
- darepublic 6mo agoReact is just JavaScript. Also vanilla JavaScript is just JavaScript
- dashersw 6mo agoReact has its own idioms like hooks, and virtual DOM operations. A developer well-versed in JavaScript and who never saw React before wouldn't know how `useState`, or a `Context` would work. They wouldn't know that components would re-evaluate and re-render all the time. They also wouldn't know they shouldn't put useState inside a conditional statement. They wouldn't know they would need a useEffect, or that it depends on an array to declare its dependencies. Vanilla JS, on the other hand, requires a good knowledge of DOM APIs. Gea tries to be as close to plain old JavaScript as possible, the way we write it on the backend. The only necessary notion is that everything is reactive and DOM will update automatically as component/store members change.
- esailija 6mo agoNot being able to call a function inside if block or a loop is not just javascript.
- ludwigvan 6mo agoReact hooks always struck me as object-oriented programming reinvented through the back door of functions. We started with pure components, decided we needed state after all, and ended up with magic functions that stash and retrieve state from some hidden context — essentially re-deriving this with worse ergonomics and an implicit ordering contract. Some part of it was the functional language paradigms like immutability that were popular elsewhere at the time bolted on to JavaScript. What I find refreshing about Gea is that it doesn't fight the language. Stores are classes. Computed values are getters. State mutation is just assignment. I've been waiting for a framework that embraces the actual paradigms of the language it's written in, rather than inventing a parallel universe of conventions. Excited to try this one. That said, I'm genuinely curious where the edges are. Was React's complexity accidental due to its architecture or was it the price of solving genuinely hard problems (concurrent rendering, suspense boundaries, fine-grained error recovery) (which by the way most consumers of the library did not care that much about)? Does Gea's simplicity hold up as apps get complex, or will we eventually hit patterns where the escape hatch is the complexity React already internalized?
- bluegatty 6mo agoThoughtful. I suspect everything about react seems like a backflip to get access to things we kind of needed in the first place. Reacts complexity I believe are due to the fact that it's an overlay onto a system with high impedance mismatch - only then some degree of inherent complexity.
- dashersw 6mo agoThank you for the thorough comments! I wholeheartedly agree. And while I believe React invented most of its problems (mostly because we've been engineering GUI solutions since mid-70's and, as I always say, Excel, the god of all UI apps, shipped in 1985) I also acknowledge modern problems like suspenses that occur as a result of elevated expectations. Gea is frankly very new, and for example doesn't ship a solution for suspenses or fine-grained error recovery yet. And since noone, including me, built a very complex Gea app yet, we don't exactly know if the simplicity will hold up. But Gea is the 3rd generation of my frontend frameworks, and I've been building vanilla JS-esque frontends since 2011 (when I released my first library, tartJS). The main feature of a good framework is to contain code complexity as the app grows and I believe as GUI engineers we have some good patterns to flatten the complexity. Gea is just trying to hide repetitive DOM updates behind a compiler, and while it has proven somewhat difficult to account for all the different ways people (or AI) write JavaScript, I'm constantly improving the compiler with new patterns. That's why Gea ships with several GUI app implementations—my approach is kind of simple. If I can get AI to generate several different UI apps in different domains, I can capture a good enough set of complexities for which I can deliver solutions. I've already fixed tens of bugs that I could only see as a result of building a UI with the library. Having said that, it's still very early for Gea. I guess only time will tell if we will have to resort to different, non-idiomatic solutions to handle more complex demands. At least the philosophy is very clear—Google built its original web 2.0 apps with Google Closure Library, an imperative UI framework, with lots of repetitive boilerplate. And that was enough to give us maps and google docs, etc., so I am hopeful for the future that we will be able to find idiomatic solutions.
- mpalmer 6mo agoThe contrast of most of the text against the background is not accessible. I'm not overly impressed with the claim "Faster than Solid" when only figure presented on the hero chart is the geometric average of the Duration scores for each framework. Digging into the individual metrics, Solid is well within the margin of error on essentially every metric to tie or even beat Gea. On top of that, Solid beats Gea handily on bundle size, 1:4 uncompressed and 1:2 compressed. So at best, Gea is a tie on speed with Solid, the bundle is bigger, and even time to first paint is a little worse.
- dashersw 6mo agoSolid is honestly a beast, and I love it! It was a great challenge to even match its performance, let alone beat it. While on several metrics they are within margin of error, select row, swap rows, remove row, and clear rows performances are significantly better on Gea's side. Having said that, pure performance wasn't the goal as much as the developer experience. I wanted to build something that felt _native_ to the language. And thank you for the comment on accessibility—I just updated the website and the docs to make the text more legible.
- mpalmer 6mo agoIt's impressive for sure! But all of those metrics differ by something like 1 millisecond, and you've only got benchmark data from an M4 Macbook Pro. On the strength of this, you promise us: "The fastest compiled UI framework — ahead of Solid, Svelte, Vue, and React." I know you've put quite a bit of work into the underlying libraries here, but this is the sort of claim people are sure to poke at. Is the Gea code used in the benchmark published anywhere?
- dashersw 6mo agoThank you! And you are right. I hadn't submitted it to the original js-framework-benchmark repo yet, but I just added the benchmark code on gea's repo. You can find it on https://github.com/dashersw/gea/blob/main/benchmark/src/benchmark.js https://github.com/dashersw/gea/blob/main/benchmark/src/benc..., and the store on https://github.com/dashersw/gea/blob/main/benchmark/src/store.ts https://github.com/dashersw/gea/blob/main/benchmark/src/stor...
- slopinthebag 6mo agoAt first I thought this was using a compiler to figure out data dependencies on regular javascript objects, including built in's on the window and coming from third party dependencies, so you could do this: <div>window width: {window.innerWidth}</div> But it's not, it's just on objects that subclass a Store.
- dashersw 6mo agoThis is an interesting idea, though, thanks for bringing it up! I will think about it and try to add it to the compiler. Especially native objects like `window` would be great to create handlers for in the compiler. It would make life really easy for the developer.
- slopinthebag 6mo agoYeah, I experimented with this a while ago, basically compile templates to get their dynamic data dependencies and then poll them for changes, although I did it at runtime. I was able to have thousands of components running with fairly minimal cost, turns out strict equality checks are pretty cheap, although there was issues with defining custom objects or anon functions inside the template. A compiler could be much smarter about this, including nested field access on objects, ignoring temporary objects and functions, and deduping checks so only the minimal amount of values would be polled each frame. Values out of the viewport can "sleep" or be polled on a lower frequency. It's kind of an interesting paradigm because it doesn't require anything on the part of the developer to write their state management in a specific way or to wrap third party dependencies in a reactive wrapper. Of course polling is not fashionable as it's seen as "crude" or "unoptimised" but typically most UI's only have a few dozen input sources at most visible on a screen, and polling that amount of data amounts to less than a ms even on slower mobile hardware. In extreme cases with thousands of data points the compiler could be smart and "short-circuit" the checks, or the component could opt into manual update calls. But it's super powerful to have what amounts to true immediate mode UI, the entire issue of state management basically goes away. `window.state` becomes a perfectly viable option haha. Anyways, cool framework.
- dangoodmanUT 6mo agoThis clearly AI generated website is such a turn-off. I get that AI can be good at making websites, and someone might not care to spend a lot of time on it, but a website that looks like a slightly modified version of a generic "make an X landing page" from gpt-5.3-codex doesn't scream "I care about what I just made". Just go super reductionist like planetscale did and have partially rendered markdown, don't put twinkling stars in the background
- dashersw 6mo agoAs the author... I somewhat agree :) But I really like synthwave and ever since I came across the design trend I wanted to use it somewhere. And I put twinkling stars on purpose, there's even shooting stars if you wait enough. I understand it comes across as generic AI slop, but this is an early project and it will evolve. I will work on a planetscale-style webpage and maybe I can add it as an option you can toggle on :)
- fatihacet 6mo ago[dead]
- Acmeon 6mo agoLooks quite cool! I have also considered similar ideas, but have decided to not develop anything. However, there are a few important areas where we have different points of view. My main use cases relate mainly to computationally demanding applications and, thus, runtime performance is quite important. First, the claim that one gets "reactivity for free" is not entirely true (although the costs may be negligible for many apps). The stores are wrapped in proxies, which lowers the performance of, for example, property access. This is why I rejected the idea of proxies and instead considered generating getters and setters to handle reactivity. This could, in principle, enable zero overhead reads (assuming that all the reactivity stuff were to be handled in the setter). However, this approach fails to account for, for example, array mutations. Thus, I see the point in proxies, but the cost of them can be significantly lowered performance (which may not matter for all applications). Second, not memoizing computed values (i.e., getters) can also have a significant negative impact on runtime performance, because expensive computations may have to be executed many times. I suppose that caching could be offloaded to the developer, but this could be laborious, at least if the developer would have to keep track of when to invalidate the computation. In, for example, Excel, computed values can be accessed fast, because they are stored in memory (although the added memory usage can become a problem). Third, you have not addressed async stores or async computed values (as far as I can tell). I will admit that considering async can be quite complicated. However, for better or worse, async is still a key part of JavaScript (e.g., sync alternatives do not exist for all APIs). Thus, the claims "JavaScript is enough", "Zero Concepts" and "You already know the entire API" are slightly imprecise, because async is not supported (although, this does not necessarily matter for many applications). These three points were the main reasons why I chose not to pursue my similar ideas (especially in the context of computationally demanding applications). Still, I find the idea that JavaScript should be enough for reactivity to be compelling and worthwhile of further development.
- dashersw 6mo agoThanks for your insights. I was originally hesitant about the performance of proxies, too, but they turned out to be great. The benchmarks (https://geajs.com/benchmark-report.html https://geajs.com/benchmark-report.html) also show good results. Both in terms of memory and CPU cycles, even though proxies are obviously adding an overhead, it's not day and night (https://jsben.ch/proxy-vs-object-performance-benchmark-dtxo6 https://jsben.ch/proxy-vs-object-performance-benchmark-dtxo6 is a good test for this). With a proxy, you can set a property 25 million times per second (on my M4 Max machine in Safari) with only 4% perf loss vs defineProperty, and Chrome is about half the perf with 20% loss vs defineProperty. So, still, 12.5 million setters per second is pretty good. Of course if your use case demands more performance, nothing beats a hand-optimized vanilla JS code. Since Gea doesn't rerender the template _at all (well, for the most part, at least)_, in theory we wouldn't really gain much from getter memoization, mainly because we create proxy observers for computed values that update the DOM in place only when the underlying value updates. And since stores are plain old JS classes, there's no need for an "async store" concept. Just update a value in the store whenever you want, track its loading state however you want, either synchronously or asynchronously, and the observers will take care of them. If you refer to another pattern that I'm not aware of, please let me know.
- apatheticonion 6mo agoNot to knock on the project - it's certainly interesting. I find it funny that the headline is "JavaSCript is enough" - yet this is a compiler on top of JavaScript that introduces magic and behavioural changes to syntax. How well does this work with testing frameworks? Can this run without the compiler? A lot of the thinking behind this compiler comes out of the box with Rust. If only wasm worked.
- dashersw 6mo agoHeh, sorry, I (the author) wasn't the one who created the post. But the idea is reactivity in JS shouldn't require new syntax. Gea works best with the compiler, I documented a non-compiled (only compiles JSX) browser usage here: https://geajs.com/docs/browser-usage.html https://geajs.com/docs/browser-usage.html but this obviously requires manual store observers and manual DOM updates, which means it's not _really_ benefiting from Gea.
- vivzkestrel 6mo ago- would be nice if it wasnt using JSX - would have preferred a syntax like svelte
- dashersw 6mo agoGea in fact supports regular HTML strings out of the box—that's what the compiler turns the JSX into anyway. However IDE tooling is still in the works for syntax highlighting regular HTML. What syntax would you prefer from Svelte? Like for hooks / stores, or rendering?
- efilife 6mo agoI click, then I see an AI generated website, and AI generated pixelart. Then I leave.
- brettermeier 6mo agoNobody is sad about that.
- efilife 6mo agoI am
- anentropic 6mo agoHow do you pronounce it?
- dashersw 6mo agoIt's like get-ah (but without the t).
- ankit7000 6mo ago[dead]
- machiaweliczny 6mo agoRookie mistake, you just conflating concepts pretending things don't exist. Good API is one that is minimal, explicit and acomplishes the job without conflating with somethings else. Congrats now I don't know if this variable is designed for rendering of state managements and when I will hit some perf issues will have no idea what causes this, same with some unintended state update or something like this. If you can do everything using same concept it's obviously harder to read as you need to simulate compiler in your head which is bad design.
- bitbrain 6mo ago[dead]