Solidjs – JavaScript UI Library
solidjs.com
solidjs.com
Wow, I just read about Solid for the first time, and I'm impressed at the API design. I love how it's a fully reactive data flow thing, but it looks and feels like React Hooks.
The other reactive/observable-based frameworks I've seen (eg Cycle) put the observable streams center piece. I always felt that was distracting, and that nuances about how the underlying observable stream library worked (eg Rxjs or Bacon) quickly got in the way.
Solid still puts the components firmly at the center, just like React, but replaces React's state concept by reactive observable state, called "signals". You use them like you useState in React, but deep inside it's an observable stream of data changes, and you get all the finegrained update control that comes with that.
I also love how noun-heavy it is. Resources, tracking scopes, effects, signals. It's just like how React moved from "do this thing after the component updated" to "ok we have this concept called an effect", but extended to more topics such as dealing with async data loading, when exactly a signal is observable, etc.
I wondered how they did this second thing and guessed that it was parsing the JS code I was writing somehow. Either that or flooding the observables with values and making note of how changes trickle down. It turned out that it was doing neither of those things but frankly I didn't understand how it worked even when it was explained to me. Might be a good time to revisit and satiate my curiosity and take another look at it.
The performance that SolidJS eeks out of the DOM is really next level.
I think it could use a small augment in the docs about migrating from React to SolidJS, but all around the project is very approachable and fantastic, and its fast
[0]: https://markojs.com/
Kind of a weird way of putting it. Intuitively any framework abstracting concepts on top of DOM manipulation has to be slower than direct DOM manipulation.
But yes in comparison to other frameworks, the benchmarks they make do look impressive.
Now I'm curious to do some benchmarking of my own.
Now React is 8 years old and browsers have improved a lot since then so I imagine the gains might not be what they used to be. But at the time it was huge.
Fine-grained reactivity existed back then and was more performant for updates. Always was. Just had its own issues since pre-MobX we didn't see implementations in JavaScript which provide glitchfree execution guarantees. So Virtual DOM was a great invention but I think it was misrepresented early on. That's what got me to start working on Solid. I knew the performance was there without a VDOM from day one. I'd seen it. So when Knockout started waning in popularity 2015/2016 I started working on a replacement.
As Dan Abramov said a couple of years later <https://medium.com/@dan_abramov/youre-missing-the-point-of-r...>, people were missing the point of React: it was never about VDOM; rather, that was a cost that at the time they reckoned had to be paid in order to write reliable code in an immediate mode style, because if you tried doing that without DOM reconciliation the result would be atrociously bad. VDOM came about because the alternative (the consistently faster alternative, I may add) entailed things like explicit DOM mutation that was far too easy to make mistakes with, and reactive data flow was generally even buggier. It was a carefully-chosen trade-off: shedding some performance, for greater robustness and ease of use.
“DOM is slow, VDOM is fast” was a straw man comparison that entered the public perception but which the React team mostly stayed well clear of: almost no serious systems have ever used the DOM directly in the immediate mode style, because it has obvious and serious problems in both performance and transient UI state like scroll and caret positions and element focus (… and transient state things are problems for all immediate mode interfaces, not just DOM ones: escape hatches are fundamentally required).
Was VDOM worth the cost at the time, compared with the other options then available? For most people, probably. And even for the rest, React presented useful food for thought that led to other options improving too. Is VDOM worth the cost now? Well, I’m with Rich Harris that VDOM is pure overhead <https://svelte.dev/blog/virtual-dom-is-pure-overhead> and that we have more efficient ways of doing things now.
Back in the day when the DOM was slow the primary performance limitation was accessing everything through a single bottleneck, the document object. To solve for this the concept of document fragments was invented. These aren't used anymore because the DOM is insanely fast and modern implementations (popular frameworks) are so incredibly slow. You aren't going to achieve a technology solution to a people problem.
The first big misconception of DOM performance is the difference between DOM interaction and visual rendering. Visual rendering is fast now because its offloaded to the GPU but its still far slower than accessing and modifying the DOM. As an example set an element to display:none and then perform what ever DOM modifications you want to it. Those changes have no visual rendering, are still DOM manipulation, and are insanely fast. You can measure this with a microbenchmark tool.
The second big misconception of DOM performance is how to access the DOM. The fastest means of access are the old static DOM methods, like: getElementById and getElementsByClassName. Query selectors will always impose a huge performance penalty when there are standard methods to do the same job and a minor performance boost when there aren't. The querySelectorAll method compounds that performance penalty. The performance penalty is present due to string parsing of the selector as necessary to convert that into something vaguely equivalent to the static methods, which is a step on each operation the static methods do not require. The minor performance to access things, such as by attribute, is achieved because there isn't a single static method equivalent and more steps must be taken compared to the parsed string result of the selector, but that performance boost is exceedingly minor (16x at most).
Usually developers prefer slower means of access to the DOM due to preferential bias to declarative approaches to programming. There isn't a performance tool to fix developer bias.
If you want both performance and less intimidating approaches to DOM access you can create aliases that solves for code reuse with more friendly names, but you will still need to understand the concept of a tree model.
Documentation > performance for most business applications, because it's developer performance. I don't have time to reverse engineer some uber nerd's SIMD optimized world wonder, I have things to ship.
All that being said. If you are happy with Mithril stick with it. It sounds like it's done everything you needed. I have a lot of respect for it's minimalist approach and its author is one of the most insightful and helpful people I've come across since getting into JavaScript frameworks.
If you are interested in trying something different. Check out our tutorials on the site and see how you feel about it. It is a little bit different type of framework.
https://krausest.github.io/js-framework-benchmark/current.ht...
IMO the best thing about Mithril is that it doesn't have reactivity, much like Imba. This allows you to define state with pure vanilla objects and classes. Also that it includes an HTTP client and router in just 10kB.
It's really verbose though compared to Svelte.
FYI, the krausest benchmark is known among framework authors to be not very good (it weighs some aspects much more heavily than others and has been gamed by various toy-ish "frameworks" that aren't all that practical in real life).
With that said, people obviously use React and even Ember (which are on the slower side of the krausest rankings) out in the wild and they're generally fine frameworks: asciinema-style "render-a-huge-grid-at-60fps" is very much a niche use case that 99.9% of people don't have.
I think the most accurate way to describe Mithril.js is that it aims to be a "get-out-of-your-way" sort of tool, in the sense that if things go wonky, you can generally reason about the low level reason as to why that is the case. For example, in Mithril.js, `render` is not just a first-class concept but an explicit API. So if you ever run into an issue where the template doesn't update for whatever reason, you can intuitively infer exactly what to do to unblock yourself. No need to reason about stale closures when debugging useEffect, observable/signal composition, or similarly complicated mental models. Reactivity, specifically, is great for squeezing performance from needle-in-the-haystack sort of updates when you have humongous haystacks, but it does also have caveats: if Svelte ever doesn't update the template for some reason, the mental model required to understand reactivity membership graphs and reactive bindings and where the compiler has jurisdiction and all that jazz are quite a bit more complicated than "ok fine, just slap a render call in this library's event handler".
The thing you said about vanilla objects follows from those principles: as a JS person, you know how objects work so you're never going to run into cognitive dissonance about the semantics of your primitives.
The verbosity thing I think is more a testament to Svelte being terse than anything else. Mithril.js isn't really verbose compared to other frameworks, IMHO.
</two-cents>
I agree with all your points, of course. I don't think I ever stated that Mithril was slow or verbose in an absolute sense.
Thank you for stating that. Seems like Mithril isn't just fast for processor, rather it is a fast for mental processing.
In more popular frameworks like React, Vue, and Angular, there are multiple tools that give you a structure. In Mithril you're free to do whatever you want.
Personally, I love that freedom. It's also one of the reasons I love Svelte, since it tends to get out of the way.
Having components only run once really simplifies things ...no need to stash useRefs and useCallbacks everywhere. Refs are simply the elements themselves.
Mostly porting required a fairly repeatable pattern of removing awkward React code and using a couple of Solid functions to keep props reactive when splitting them up to spread across JSX elements.
A+ for Solid developer experience (coming from 5+ years working with React). Oh, and performance/size are added benefits.
From the docs; does SolidJS provide a way to lint or warn on this? I've been getting more and more scared of destructuring and ... copying recently since, for example, you lose prototype info when you do this. TypeScript doesn't warn you.
Well, certainly it should be clear that `obj !== {...obj}`, and you have to behave accordingly.
In reality though, assuming you are destructuring to pass it down a tree (and not around the app), this usually just means that you lose the optimizations of only rendering some part of the subtree and render your whole subtree more often, which is equivalent to using state management that isn't integrated into the scheduling engine very well, which is very common. So you're just going from a nicely performant app to standard fare.
I would recommend to always try to think in a singleton structure and use IDs and maps to the original objects over de- and re-constructing things you pass around as if they were the original
Sure but that's not my issue. I'm saying that when you copy an object like you just did, the latter object looses its prototype.
So any prototype methods I try to call on a copy/de structured object like that will crash in my app without a prior TypeScript warning.
I want to be able to copy objects with a nice syntax but still have them retain their prototype.
Since no one warns you (TypeScript anyway) about losing your prototype it makes me worry about this everywhere because who knows which objects weren't meant to lose their prototypes.
> Sure but that's not my issue
I think you misunderstand what I'm saying. I know you know `obj !== {...obj}`, but it's important to understand exactly what that means, and one of those things to understand is
> when you copy an object [via destructuring], the latter object looses its prototype
For example, any class instance should most certainly not be used in that way, as it moves away from a true "object" paradigm (keys and values baby) into an inheritance paradigm, and as you observed inheritance is lost in destructuring.
> TypeScript doesn't warn you
I find this hard to believe. If you are passing TypeScript some interface T, and the object {...tInstance} doesn't have the keys of T, you should get an error. If you are passing TypeScript some class X, and you try to claim `typeof {...(xInstance)} === X` you would also surely see errors.
Please link an example so I can understand what I mean, I would guess you didn't type the destination very stringently so the loss of the class type was unobserved
I wonder what I was seeing in my code...
I think the term is used loosely here to suggest that they are wrappers on top of objects. It isn't so much about the specifics but to explain why destructuring should be avoided.
const valueGetter = () => props.value;What is interesting, render function really is a function factory - like reagent form-2. That let you simplify "hooks rules", because they're called once and also let you factor your components more easily without much consideration for cost of the component abstraction.
My favourite feature is "opt-in" reactivity. In bigger data visualisations you must be conscious what would re-render in response to what change. If you have only component boundaries like in React it is very easy to waste re-renders and then you start adding useMemos and React.lazy. In Solid I can start from opposite side, and declare granular computed properties and Solid will take care of all re-renders.
It has few rough edges like special props object or "boilerplate" function calls. But that's some minor ergonomic issues.
In conclusion, thanks Ryan!
[1] 4x smaller, 50x faster, https://blog.asciinema.org/post/smaller-faster/ https://news.ycombinator.com/item?id=29387761
I cannot understand how some libraries are so popular without changing that much and others are not popular at all.
JS development would benefit greatly from a native “immutable, nestable, performant, deep-compare-by-value” data type that supported something like these operations:
atom.get(path)
atom.set(newValue) // returns new atom
atom.set(path, newValue) // returns new atom
By handling this type in the JS engine, you can take advantage of HAMT to make this quite performant[1]. The “single-global-state atom” pattern is a point of convergence, and it would be nice to have native speed instead of relying on one of the many libraries that reach for it, each with their own trade offs. (SolidJS uses proxies for this.)It should be a language-level abstraction. I suspect it would be rapidly adopted by a lot of frameworks, and we’d all benefit from it.
[0] https://javascript.plainenglish.io/designing-solidjs-immutab...
I think this is kind of the thing with innovation. Innovation is anarchy until the point where the innovation is accepted as best practice. And at that point it's not really innovation. It's just best practice.
Something a bit more advanced than fetch, not quite as complex as useContext. To the end user, websites haven't really changed all that much since the early AJAX days. Input gets received, ajax happens, shit gets updated. Why has the tooling gotten 1000x more complex?
But with ES standards, at least the code can be largely the same, not as different as vanilla/jQ/React/Angular/Svelt/Vue/someotherslickonewordjsframeworkdujour
To be fair to standards bodies, they have done some work. Element.append now exists to make hyperscripts a bit more straightforward, and a lot of reactivity semantics can be implemented on top of Proxy.
Great job.
In the future, people might build UIs for Solid using a (hypothetical) solid-solid or at least combining Solid UI and SolidJS: e.g. https://github.com/solid/solid-ui vs https://github.com/solidjs/solid
At least, if SolidJS is supposed [1] to be a name separate from Solid, why not call the repo solidjs like the org?
I can already imagine the naming discussions if we were to build a Solid library for Solidjs. "Solid-ui-solidjs"? "Solid-client-solidjs-js"? Oh boy. We're currently using React for most things, but I could imagine switching to this if it takes off... that's going to make following conversations very confusing, haha.
Now that I say that and I've clicked around, I wonder which is canonical: "Solidjs", "SolidJS", or "Solid.js"? I've seen it all three ways.
That being said the work has already started on a starter with Nested Routing/Automatic File Based Routing + Code Splitting/Parallelized Data Fetching/Streaming SSR/Multiple deployment adapters. We're given it the same focus on performance that we've given the rest of Solid.
Here is the recent Vercel Edge Function demo we made with it: https://twitter.com/RyanCarniato/status/1453283158149980161
I do agree that Marko is quite promising. I think quite a few sites that need server rendered markup and progressively enhanced features could be implemented more simply in Marko than the equivalent solutions with React, Svelte or anything else.
Solid does not have Hooks or Hook rules. They look similar but execute more similar to say Svelte. Although Svelte still is about Component re-renders and Solid's reactivity is more granular hence the performance improvement.
This area of DSLs is very superficial for the most part. I think people have preferences and I'm exploring both sides between Marko and Solid but saying things like unintuitive I think mischaracterizes things. Maybe explicit, transparent, transferrable, and composable are better adjectives that apply more to Solid than to Svelte that you can use in the future.
𝚊̶𝚜̶ ̶𝚖̶𝚢̶ ̶𝚌̶𝚘̶𝚖̶𝚙̶𝚞̶𝚝̶𝚎̶𝚛̶ ̶𝚔̶𝚎̶𝚎̶𝚙̶𝚜̶ ̶𝚏̶𝚛̶𝚎̶𝚎̶𝚣̶𝚒̶𝚗̶𝚐̶ ̶𝚠̶𝚑̶𝚒̶𝚕̶𝚎̶ ̶𝚕̶𝚘̶𝚊̶𝚍̶𝚒̶𝚗̶𝚐̶ ̶/̶ ̶𝚜̶𝚌̶𝚛̶𝚘̶𝚕̶𝚕̶𝚒̶𝚗̶𝚐̶ ̶𝚝̶𝚑̶𝚎̶ ̶𝚜̶𝚒̶𝚝̶𝚎̶ ̶(̶𝚖̶𝚊̶𝚌̶𝚋̶𝚘̶𝚘̶𝚔̶ ̶𝚙̶𝚛̶𝚘̶ ̶𝟸̶𝟶̶𝟷̶𝟻̶ ̶𝚠̶𝚒̶𝚝̶𝚑̶ ̶𝙵̶𝚒̶𝚛̶𝚎̶𝚏̶𝚘̶𝚡̶)̶
It's also not very clear what exactly solidjs is. My understanding is that it's basically React but with a different method to render components?
Otherwise the site is actually impressive and the UX is really good.
EDIT: Sorry this actually seems to be my computer's fault because other sites are slow too, even HN is a bit jittery. Idk if I have too many tabs open.
I can say that the site particularly slows down whenever I'm scrolling and especially when scrolling while an animation plays. Changing tabs is very fast even though there are a lot of animations.
SolidJS is a UI library, that is basically a reactive state library first, renderer second. It happens to look like React by choice, since it chooses JSX for its flexible composability and React Hooks resemble reactive primitives. Cliff notes are reactivity is independent of components. Components are just functions that run once and wire up granular updates. Then only the things that change ever re-run.
SolidJS is a framework at the abstraction level of react, vue, svelte, or marko, with an API that adopts certain philosophies from react. It is an entirely incompatible framework, however, and not just a drop in renderer (though easier to port to from react due to api similarities).
There is very little we can do about this once we do go to load, it's just the nature a heavy fully featured editor like Monaco.
It's got some syntax similarities, in sort of the same way that most of the C-family languages look similar (for those languages, curly braces, if statements, semicolons, declaring data types; for Solid and React, function components and JSX syntax), but that's it.
FWIW, from my own viewpoint in the middle of the React ecosystem, I think Solid looks like a fascinating approach and it has a bunch of reasons to be worth considering in its own right. Doesn't mean it'll magically gain adoption or ever be considered one of the major players in the web framework space, but it's definitely far more than just a random toy project.