Solid – A declarative JavaScript library for building user interfaces
github.com
github.com
Edit: I see the author is here, and lots of typically negative comments are being thrown at him. Ignore them and keep up the good work! We need people like you to push performance forward.
I'd just point out that for the average project, the main advantage is that with something like Solid or Svelte, it will load in less than a second, vs. a few seconds for React, especially for something simple like a blog where you don't want to think about perf or optimize much.
Still, I'm tired of using a normal web application and feeling the UI lag when I type, etc. I suspect that if you start off with a project like Svelte or Solid, you'll have a longer runway on your app before you get to that level of suck.
If you're wondering how this is different from Svelte, the author has a Medium post on it[0].
[0]: https://medium.com/@ryansolid/javascript-ui-compilers-compar...
React can easily be used to render just the parts of the page that need it, with all the surrounding html done the old fashioned way.
React works well until you try to avoid re-rendering and then it really falls appart. For example, updating a context re-renders the whole component chain from producer to consumer, and trying to block useless re-renders with shouldComponentUpdate easily leads to mistakes where children don't update when they should. Since React rendering use lazy evaluation of JSX expressions, tracking who exactly updates and when is much harder than what it might seem initially (akin to tracking mutations in a language with lazy evaluation). As this library actually tracks dependencies and only re-renders what you want, all of this bloat is avoided. One aspect that impresses me in particular is that JSX expressions actually return real DOM nodes, making it easy to keep references to them for imperative operations (not a big deal but so much nicer than React refs).
I'm wondering why you opted for the setState API rather than a nicer imperative API like Mobx. From what I can gather, state changes are tracked by diffing? Why not just return a proxy?
I'm pressing this up triangle as hard as I can, but sadly it still lets me upvote this only once.
Also don't be worried about copying the array that much. I'm doing that in the JS Framework Benchmark and it's still about the fastest with 10k items. The real key is identifying where you don't need to update the list at all just the items in it. That being said you can do an update without copying the array like this:
setState('list', state.list.length, newItem);
Granularity of performance isn't a silver bullet. Being fine-grained doesn't necessarily make everything faster (especially creation). The power is that granularity is arbitrary so it can be maximized to the type of situation, completely independent of component structure.
`setState('list', state.list.length, newItem);`
over
`list.push(newItem);`
Considering that the two operations can be otherwise equivalent when using proxies (from what I understand).
You can use the "Immer" syntax of setState if you want:
setState(s => { s.list.push(data); });
But in some cases it is more expensive. Like a splice will per row will trigger the proxy to tell you they've all shifted a position. Where as just setting the array only hits the proxy once. Sure Solid batches the updates from the call but it's still unnecessary tracking. Solid lazily decides whether state should be made into a reactive atom based on whether it is referenced in a tracking scope.
Personally, I was originally introduced to it by someone in the Mithril.js gitter chat, and think it's a great project. I wish the Solid team the best and look forward to trying out Solid in my next toy project. The performance benchmarks and relatively clean API are truly impressive.
But it wasn't always like this. Leo Horie from Mithril was always supportive early days when it seemed I only was receiving this sort of "feedback" on reddit if any at all.
Unfortunately, JS fatigue is real and it can be challenging for someone who has their hands full with just relearning new React idioms to also evaluate the dozens of other libraries out there, so it's easy to miss a gold nugget.
For those who are not seeing the value proposition here, think Svelte, but where the "reactivity magic" is encapsulated via Components to still allow React-like "it's-just-JS" component code, while also getting rid of a lot of runtime overhead and the complexities associated with trying to optimize a virtual dom (as has been the recent direction for React).
One great benefit over Svelte with this approach is that you can use Typescript in templates (something that Svelte still is not able to do).
Solid's performance claims to fame are also legit (i.e. it doesn't sacrifice good devexp, good engineering, unlike many of the top performers in the stephen krause's benchmarks).
The only thing I wish was better is docs. It starts off "spilling its guts", so to speak, and while that is an ok way to explain why Solid is different than others and to garner interest from library authors, it's also not super beginner friendly. One cannot, for example, easily find docs for `<For>` even though it's analogous to core syntax if we were to compare to a programming language. Putting a follow-along tutorial upfront would go a long way.
Now JS libraries advertise themselves based having no VirtualDOM.
Can someone explain this to me?
but theses next generation frameworks are showing that there's a better way to solve the same problem.
Basically, it boils down to moving from tracking the DOM state in a VirtualDOM to tracking what DOM updates can happen at the compile stage and then just doing those exact updates to the DOM.
Even if that could be proven, code bloat is still a problem. With a vDom library, the render engine size is fixed. Moreover, those functions are guaranteed to run enough that they will be optimized by the JIT while changing render functions for every component could mean your renders are optimized for this view, but back in interpreter land when rendering the next view.
You're probably thinking of Concurrent Mode, which the talk does indeed address. Concurrent Mode is, among other things, a clever way of solving one of the problems introduced by the virtual DOM paradigm: that you have to rerun a lot of user code on every state change that will often block the main thread if you don't break it up into chunks.
More on virtual DOM here: https://svelte.dev/blog/virtual-dom-is-pure-overhead
Now don't get me wrong I've benchmarked Hooks like crazy and I don't think they are much consideration on performance at all. It's just the mental model of always update on the outside with bubbles of things that don't update creating these closures is a little unnatural. By comparison the mechanism they ape (fine grained reactivity) works exactly the opposite way. You just need to be aware the stuff outside doesn't update. Wrap what needs to be updated. Done. There are no out of date closures. No inconsistent mental model. It's as straightforward as registering event handlers.
Still I have to admit the solution is genius. They have managed to get 90% of the benefits with simply repositioning things. At one point this was one of my biggest arguments against React. People didn't appreciate it before hooks. But can you imagine knowing you could write applications this way years ago and trying to convince someone using React classes there was a better way? I just sort of gave up and did my own thing.
in particular, i don't quite get this bit:
> By comparison the mechanism they ape (fine grained reactivity) works exactly the opposite way. You just need to be aware the stuff outside doesn't update.
inside/outside of what?
PS. my yet undeveloped pet theory is that hooks are (somehow) something like half a monad/algebraic-effect-thingy... though they're probably too tied up with the render cycle to analyze them this way
1. Make all updates manually with jQuery. This is fast, but hard to keep track of.
2. React, create a virtual DOM, then compare the current DOM with the virtual one, and figure out what needs to change.
3. Solid, Svelte, don’t create a virtual DOM, but have the JS compile all the possible changes so you can make them directly in the DOM like with jQuery.
In this case, there's no evidence that precompiling is faster in theory -- let alone in practice. In the JS framework benchmark suite, SolidJS and InfernoJS performance is almost identical (with SolidJS having a much larger margin for error in most tests).
This is the BEST possible case for precompiling too. In the real world, JITs take a long time to warm up (a couple hundred executions before all the optimizations kick in). With the vDom, you warm up ONE set of code that then runs forever. With the precompile, it has to warm up for EVERY new component and potentially slightly different codepaths within the same component.
The JS framework benchmark reuses the same components for everything which is a huge advantage to precompiled frameworks while not having much impact on vDom ones (as the actual components in both cases usually won't optimize very much due to being polymorphic).
It’s absolutely not because it requires far greater effort computationally. The benefit has nothing to do with performance but instead simplified state management.
I know people desire certain frameworks due to how they perform state management. I have never really understood that motivation myself though because managing state is incredibly simple. Here is a basic outline of how simple it is:
1) realize there are exactly two facets to every component: data, interface.
2) all components should be stored in common locations. A single object stores component data and a common DOM node for storing component interfaces.
3) pick a component facet to update, either data or interface, and never update the other. The other should be automatically updated by your application logic (reflection).
4) store your data on each change. That could be dumping it into local storage. In my current app I send the data to a local node instance to write into a file so that state can shared across browsers in real time.
5) be able to restore state. On a fresh page, or even page refresh, simply grab the stored data and rebuild the component interfaces from it.
My current application is a peer to peer Windows like GUI that works in the browser and exposes the file system (local device and remote devices) in Windows Explorer like interfaces. Managing state is the least challenging part of this. The slowest executing part are long polling operations against large file system trees (it’s about as slow in the native OS interface)
I mean I agree with most of your post, but I'm not sure I would necessarily make that highlighted claim from the benchmark results. I mean the +- seems to be pretty run dependent for most libraries on there. And while I agree that the differences in performance is neglible, there is one. Solid is clearly faster in most tests even if by a small amount. Anyone interested you can look at: https://krausest.github.io/js-framework-benchmark/current.ht... And then isolate Solid and Inferno and then do a comparison against one library. It will color highlight the degree of certainty the difference is between the libraries in terms of significance of the results.
https://stackoverflow.com/questions/21109361/why-is-reacts-c...
But it isn't something that completely gets abstracted. There is a reason it has been so difficult to make a non-virtual Dom version of React. It isn't impossible, but has yet to fully flesh out despite attempts by a few projects including members of the React team.
The link has been posted in another comment too, here are some chunks that may be relevant to you:
> A virtual DOM is nice because it lets us write our code as if we were re-rendering the entire scene. Behind the scenes we want to compute a patch operation that updates the DOM to look how we expect. So while the virtual DOM diff/patch algorithm is probably not the optimal solution, it gives us a very nice way to express our applications. We just declare exactly what we want and React/virtual-dom will work out how to make your scene look like this. We don't have to do manual DOM manipulation or get confused about previous DOM state. We don't have to re-render the entire scene either, which could be much less efficient than patching it.
I don't believe this is true.
VirtualDOM is a means to an end for React. People love that "end", but they do not love the means by which it is achieved.
Maybe one could argue that people love VirtualDOM because it allowed this "end" to be achieved, but I still think every React fan would much prefer it if was doable without VirtualDOM.
It is actually "doable" without vDOM but doing so would be much less scalable and require more care from the user of the library.
I would bet a surprising portion of React's userbase wouldn't even know what the virtual DOM is.
So, in theory, you get VDOM goodness, without the bloat.
For example: suppose I want to take an arbitrary JSON structure, walk the entire JSON tree, and render it on the page as a tree of nested elements - i.e. a new subtree of divs for each array or object. None of that layout is known ahead of time, and it can take any shape - be any depth, breadth, etc. Intuitively it seems like very little can be known ahead of time for compilation purposes. Can Solid handle this case? Genuinely curious.
So if this is your primary use case where there aren't really pre-existing templates, a fast VDOM is probably slightly better if you aren't doing much in the way of updating. But hard to say. There is a runtime HyperScript version of Solid that is still blazing fast, but it is probably slightly slower overall than Inferno since it can't leverage the pre-compilation of JSX or JIT compilation the Tagged Template Literals.
Or are you saying you want a layout rendered from JSON without first defining the possible components as Svelte/Solid/React components etc? As I think that defining components explicitly is a central part of all of the front-end frameworks, compiler or otherwise?
list2 = bubble_sort(list1)
render(list2)
Now suppose the user deletes one item in list1.The question is now:
Will the update trigger a new bubble sort?
Will the update do something smarter, like just delete the DOM element?
I imagine in your scenario the psuedo code to achieve what you want would be written.
list2 = bubble_sort(list1)
removeItem = () => delete list2[random index]
...
<for each list2 render div>
The compiler will see a change to the list2 variable anywhere that removeItem is called and annotate the code to make the appropriate changes to the dom in the function call and the bubble_sort line will never be re-run unless it was explicitly added to the removeItem function.It's like Svelte without abstracting the update mechanism. Which makes it a little more to get into at first but very similar to a library like React. The difference from React is that Solid knows exactly what you are updating so it doesn't bother with the other stuff.
Also, I see it's small in size and ES module friendly. Very nice.
While I presume writing specs is a boring collateral, a friendly / bitesized / WISYWYG documentation would make a world of difference for the enthusiastic but less-than-ninjas of us.
The smarter kids can catch up faster, and some might not bother, already do react/vue. I personally found with Svelte a way tamer learning curve.
Also, polished product and polished code deserve a polished set of docs. Just sayin'!
I think a cool thing would be if the docs site was made in it. Self documenting.
I joined the Svelte camp a while ago so I'm unlikely to try Solid anytime soon, but had I started with it I wouldn't complain, because the value proposition is the same: an abstraction which is runtime-inexpensive both in terms of CPU usage and bundle size.
Svelte and Solid are actually the pinnacle of the "2nd age of JS".
(Disclosure: I work at Inrupt, the company started by Tim Berners-Lee to support the project's adoption.)
In 1983, a database was named SolidDB: https://en.wikipedia.org/wiki/SolidDB
etc. etc.
[1] https://solidproject.org/for-developers/apps/first-app/4-dat...
The closeness with the React design makes it better than svelte imo.
Does the state thing have the same gotchas as react hooks? Not a big fan of how that's impl'd in react.
That being said for the same reason. No React Compat. The library doesn't work even remotely similar. People have used them together, but this is no straight swap in.
If anything there is just so much to document.
Also if anyone has used it, do you have to use this pattern if you have derived state?
setState({
displayName: `${state.user.firstName} ${state.user.lastName}`
})
If you simply do `const displayName = ...` in the component, will the component update properly?If you want to have derived state. Simply wrap it in a function or use it in a JSX binding.
Ex.
const displayName = () => `${state.user.firstName} ${state.user.lastName}`
return <span>{displayName()}</span>;
or:
return <span>{`${state.user.firstName} ${state.user.lastName}`}</span>;
or:
// this expensive to calculate:
const displayName = createMemo(() => `${state.user.firstName} ${state.user.lastName}`)
return <span>{displayName()}</span>;
The tradeoff is rendering isn't particularly special. So the systems consciousness of specific render specific concepts requires additional consideration. There is no "onMount". There are reactive lifecycles but they aren't particularly tied to DOM updates.
Lack of devtools is expected for a new framework, however, it is worth asking for, especially if you are considering it for production
CSS - A declarative, domain-specific language for styling user interfaces.
I understand the sentiment about reinventing the wheel, but this is more like inventing tyres I suppose (fortunately they are made of rubber, so I can stretch the analogy pretty far, haha). The wheel is there, but there is something to improve to cover more use cases.
That link tag above, is not only the poster boy of interactivity, it the the foundation of the web.
> Neither allow you to build interactive user interfaces besides what is provided by the browser.
Neither does JS, or any framework built on top of it. All types of interactivity in a browser are provided by for by the browser.
Better?
Long answer: It is worse, and it all boils down to two phrases which I classify as "marketing speak". Marketing speaks aim is to impress and influence you, "technical speaks" aim is to inform and educate you. When shopping for a library or framework for your next feature or project, it helps to distinguish between the two. Usually any time you see a buzzword, you've stumbled upon some marketing speak. The buzz-wordy term "declarative" has been popularized by React, so I just thought I'd apply it to HTML and hopefully encourage someone to skill up on the web fundamentals (HTML, JS and CSS) before tackling JS frameworks to avoid disasters like this https://stackoverflow.com/questions/42464888/how-do-i-change....
> highly-interactive
What does this mean? Are you talking about video games? Because that is what comes to mind when I see the term "highly interactive".
> logic-driven
Aren't all programming languages logic driven? Or are there some feelings-driven apps?
DOM manipulation is expensive, so a check is made to see if it really needs to be done.
If you're guaranteed to need an update, then this check is pointless. But this depends on what you're doing. Its really not something that's tied to the technical identity of any library. React, vue, etc, could probably also skip this step if they wanted.
Also, this will almost never be the bottle neck. It's really low down on a long list of other things that will have a bigger impact. Web dev is in a really bad state - you're supposed to have something like 1.5k nodes according to lighthouse because of all junk that browsers have to do now. That's a puny number of checks for the cpu to do.
Of course, if everything was done perfectly I'd imagine an 80486 CPU should be able to render most of today's websites in 0.5 secs but... it's not done perfectly. :)
I like Svelte quite a lot, and I like the idea of the OP's library as well but I wouldn't count React dead just yet. It has a really good idea and the general ergonomics are there. But we shouldn't deny that it still has a room for improvement.
Also, does lack of vdom mean a project like react-native could never really come from this?
If two things change that both trigger a third thing to change, how does this efficiently prevent the third thing from updating twice needlessly? I'm imagining this becoming an exponential problem in real apps, where hundreds of components are re-updated needlessly at every UI interaction.
I generally find those far more readable than JSX (and is one of the reasons I think SwiftUI and Flutter code looks nicer than React).
Solid's JSX does not compile to HyperScript like React. HyperScript is a different slightly de-optimized experience.
Webscript does work with SolidJS nicely.
Webscript looks really cool, but it doesn't look very TypeScript-friendly.
With new projects and languages and frameworks a good angle to evaluate is, "what's the debug story like?"
It never is that simple mind you. I have an open issue around Dev Tools etc... We need to do better given the expectations of modern JS libraries.
The update cycle is the exact opposite to React Hooks. With React your Component needlessly re-renders over and over and your Hook dependencies explicitly whitelist change. With Solid your Comoponent is a basic function that executes once closing over state with it's Hooks, which are the only thing that run over and over as needed when state changes.
Doesn't sound very promising tbh.
Why not also use proxy to 'set' and 'notice update' on the state?
Would love to compare notes - you can check out (very much WIP progress) at https://github.com/sreekotay/reefer
It's been only a few weeks of fun... but REALLY interesting problem space. I use a DOM memo-ish approach to help with performance...
Solid actually can work runtime only too but I do put most of my effort on the compiler since I can use it to overcome most of the classic shortcomings of the reactive approach performance wise (and DX wise) with a bit of consideration.
Now only name it template separate it out into its own file, and build an "engine" around it. Then we are almost back, where we came from with backend rendering, which we should be doing anyway.
I've yet to see the JS framework that takes usability seriously and outputs a noscript block (during development) for me to copy paste and get the functionality for anyone disabling JS.
As it stands many web devs only see the JS enabled side of things and then are too lazy to, or not capable of implementing the noscript part. Or they are not even aware of the white page one gets for their oh so cool React apps, once JS is deactivated. It is very unprofessional amd exclusive, I must say.