It's a component independent state management system that works off minimal language (signal, computed, effect) to guarantee consistent glitch-free synchronization of state and view.
This might sound like a lot of things admittedly. But for Angular specifically this means the ability to eventually deprecate Zone.js as Signals allows direct notification of what changes in the system without dirty checking everything and sits more naturally in templating solutions than things like RxJS that require direct pipe. If Rx is for async reactivity, Signals are for synchronous.
Solving the diamond problem. We can make guarentees on execution propagation. The core team was talking to me a bit on stream about this problem of over fetching from APIs at times. Also it removes the need for the special mechanisms around Direct Pipe to my understanding. The auto tracking form is easier for templating as you can just drop expressions in.
The diamond problem and sync vs async are tangential. Sync vs async can be a stylistic implementation choice, and although Solid has synchronous effects by default, primitives can be built on them that are totally async, including createAsyncSignal and/or createAsyncEffect (implementation left to the imagination).
Personally, my stylistic choice, were I to implement something from scratch, would be for JavaScript microtasks (queueMicrotask) to be the smallest unit of reactivity time, therefore making reactions (effects) always "async" (from the perspective of JavaScript code in general) by default: modify a signal, the effect for that signal is always in the next microtask. I would pick this as an out-of-box design because it inherently prevents too many things from running. In essence it would be similar to timing of ResizeObserver, MutationObserver, etc, which are async, and the purpose is to avoid unnecessary extra work.
Solid does have its own microtask-like timing within a Solid component, where it will not execute effects until synchronous code of a Solid component tree is done running. It is very similar to microtasks, but scoped only to Solid.js component execution. When integrating Solid.js code with external libraries, it becomes apparent that Solid.js runs effects "async" relative to its component tree, but not relative to JavaScript code in general.
These things are stylistic choices: a hypothetical like Solid.js alternative, with the same essential developer experience of signals and effects, can exist but be completely async based on microtasks. Signals and effects moreso decribe the DX pattern, but not the timing of the pattern.
As a parallel example using event emitter patterns, it could very be possible that one implementation of an event emitter runs event handlers synchronously (right when events are emitted) and some other event emitter implementation may always run event handlers in a future task (f.e. in a JS microtask). This doesn't fundamentally change the fact that either way we have an event emitter pattern. We have the same pattern, implemented with different timing semantics.
It's basically Solid with different opinions - looks like you'd agree with this one.
Unfortunately it's a one man project, and not really built to complete with Solid - although it could have been. I think we need more competition in this space to explore the pros and cons of various ideas and opinions.
In simple terms: signals are things that hold a value; reactive variables so to speak. Effects are blocks of code (defined with createEffect in Solid) that automatically re-run when any signals accessed inside of them change.
The way that this helps with practical performance, compared to React for example, is that JSX is compiled into effects that update DOM directly in the most efficient way possible, without an abstraction like vdom (more on the difference below). To demonstrate very simply, the following Solid.js JSX code:
const[count,// gettersetCount// setter]=createSignal(0)setInterval(()=>{// Increment count every second setCount(count()+1)},1000)createEffect(()=>{// log count whenever it changesconsole.log(count())})// This HTMLDivElement will be updated every secondconstdiv=<div>count: {count()}</div>document.body.append(div)// It is DOM, not vdom.
compiles to essentially something like this:
const[count,// gettersetCount// setter]=createSignal(0)setInterval(()=>{// Increment count every second setCount(count()+1)},1000)createEffect(()=>{// log count whenever it changes (runs once initially)console.log(count())})constdiv=document.createElement('div')createEffect(()=>{// This HTMLDivElement will be updated every second (runs once initially)div.textContent=`count: ${count()}`})document.body.append(div)// It is DOM, not vdom.
If you don't have a build tool in place to compile JSX, you can use Solid.js's html template string tag function. The JSX part would be written like this:
// This HTMLDivElement will be updated every secondconstdiv=html`<div>count: ${count}</div>`document.body.append(div)// It is DOM, not vdom.
Here is that same example on CodePen, using no build tools (plain HTML and JS):
One more example of buildless Solid.js usage, making a function component:
How does this compare to vdom? In particular, how does it compare with Angular? Vdom is a diffing approach: every time React/Preact/Lit components update, they create a fake tree representation of the DOM, figure out the difference between that and the previous fake tree, then apply the differences to DOM. See this Angular article for more on that. TLDR, direct updates to the DOM (essentially like I showed in the example compile output) are memory efficient because they don't create trees (that need to be garbage collected) on every update. Now imagine you update in an animation loop: with vdom you'll be making a lot of garbage to collect, potentially tanking performance, and will be forced to either move your animation out of the declarative markup (not ideal for dev experience) or try to optimize your template by splitting it into the smallest components possible (not necessarily ideal if you didn't have a performance problem and otherwise didn't really need to make separate components).
By not using a VDOM, and updating the DOM directly using the most efficient methods, Solid.js achieves more performance. We can see (from that previous Angular article) that although Angular Ivy does fine grained updates -- a conceptually a better approach just like what Solid.js is doing -- benchmarks show that it still isn't as fast as it can be (I don't know exactly why, but probably due to the way change propagation is implemented, maybe related to Zone.js that Ryan mentioned, or something else, but it would be worse with the vdom in place).
The JSX compile output is actually more advanced than my naive hand-written output example. For example, in the expression <div>count: {count}</div>, Solid.js JSX output actually creates two Text nodes, one for the "count: " string, and one for the dynamic value, then updates only the text node that needs updating. The compiler has various sorts of optimizations, whereas my simplified hand-written output example is a simpler un-optimized concept that is more typical of hand-written code and is useful for describing what Solid.js JSX compiles to in a simplified way.
Some comments have been hidden by the post's author - find out more
For further actions, you may consider blocking this person and/or reporting abuse
We're a place where coders share, stay up-to-date and grow their careers.
It's a component independent state management system that works off minimal language (signal, computed, effect) to guarantee consistent glitch-free synchronization of state and view.
This might sound like a lot of things admittedly. But for Angular specifically this means the ability to eventually deprecate Zone.js as Signals allows direct notification of what changes in the system without dirty checking everything and sits more naturally in templating solutions than things like RxJS that require direct pipe. If Rx is for async reactivity, Signals are for synchronous.
If you have async reactivity already, why do you need sync reactivity? What is it for? When does it matter?
What's lacking with RxJS in the context of Angular for example?
Solving the diamond problem. We can make guarentees on execution propagation. The core team was talking to me a bit on stream about this problem of over fetching from APIs at times. Also it removes the need for the special mechanisms around Direct Pipe to my understanding. The auto tracking form is easier for templating as you can just drop expressions in.
The diamond problem and sync vs async are tangential. Sync vs async can be a stylistic implementation choice, and although Solid has synchronous effects by default, primitives can be built on them that are totally async, including
createAsyncSignaland/orcreateAsyncEffect(implementation left to the imagination).Personally, my stylistic choice, were I to implement something from scratch, would be for JavaScript microtasks (queueMicrotask) to be the smallest unit of reactivity time, therefore making reactions (effects) always "async" (from the perspective of JavaScript code in general) by default: modify a signal, the effect for that signal is always in the next microtask. I would pick this as an out-of-box design because it inherently prevents too many things from running. In essence it would be similar to timing of ResizeObserver, MutationObserver, etc, which are async, and the purpose is to avoid unnecessary extra work.
Solid does have its own microtask-like timing within a Solid component, where it will not execute effects until synchronous code of a Solid component tree is done running. It is very similar to microtasks, but scoped only to Solid.js component execution. When integrating Solid.js code with external libraries, it becomes apparent that Solid.js runs effects "async" relative to its component tree, but not relative to JavaScript code in general.
These things are stylistic choices: a hypothetical like Solid.js alternative, with the same essential developer experience of signals and effects, can exist but be completely async based on microtasks. Signals and effects moreso decribe the DX pattern, but not the timing of the pattern.
As a parallel example using event emitter patterns, it could very be possible that one implementation of an event emitter runs event handlers synchronously (right when events are emitted) and some other event emitter implementation may always run event handlers in a future task (f.e. in a JS microtask). This doesn't fundamentally change the fact that either way we have an event emitter pattern. We have the same pattern, implemented with different timing semantics.
The same applies here.
You might want to have a look at Voby -
github.com/vobyjs/oby#effect
It's basically Solid with different opinions - looks like you'd agree with this one.
Unfortunately it's a one man project, and not really built to complete with Solid - although it could have been. I think we need more competition in this space to explore the pros and cons of various ideas and opinions.
In simple terms: signals are things that hold a value; reactive variables so to speak. Effects are blocks of code (defined with createEffect in Solid) that automatically re-run when any signals accessed inside of them change.
The way that this helps with practical performance, compared to React for example, is that JSX is compiled into effects that update DOM directly in the most efficient way possible, without an abstraction like vdom (more on the difference below). To demonstrate very simply, the following Solid.js JSX code:
compiles to essentially something like this:
Example in Solid.js playground
If you don't have a build tool in place to compile JSX, you can use Solid.js's
htmltemplate string tag function. The JSX part would be written like this:Here is that same example on CodePen, using no build tools (plain HTML and JS):
One more example of buildless Solid.js usage, making a function component:
How does this compare to vdom? In particular, how does it compare with Angular? Vdom is a diffing approach: every time React/Preact/Lit components update, they create a fake tree representation of the DOM, figure out the difference between that and the previous fake tree, then apply the differences to DOM. See this Angular article for more on that. TLDR, direct updates to the DOM (essentially like I showed in the example compile output) are memory efficient because they don't create trees (that need to be garbage collected) on every update. Now imagine you update in an animation loop: with vdom you'll be making a lot of garbage to collect, potentially tanking performance, and will be forced to either move your animation out of the declarative markup (not ideal for dev experience) or try to optimize your template by splitting it into the smallest components possible (not necessarily ideal if you didn't have a performance problem and otherwise didn't really need to make separate components).
By not using a VDOM, and updating the DOM directly using the most efficient methods, Solid.js achieves more performance. We can see (from that previous Angular article) that although Angular Ivy does fine grained updates -- a conceptually a better approach just like what Solid.js is doing -- benchmarks show that it still isn't as fast as it can be (I don't know exactly why, but probably due to the way change propagation is implemented, maybe related to Zone.js that Ryan mentioned, or something else, but it would be worse with the vdom in place).
The JSX compile output is actually more advanced than my naive hand-written output example. For example, in the expression
<div>count: {count}</div>, Solid.js JSX output actually creates twoTextnodes, one for the"count: "string, and one for the dynamic value, then updates only the text node that needs updating. The compiler has various sorts of optimizations, whereas my simplified hand-written output example is a simpler un-optimized concept that is more typical of hand-written code and is useful for describing what Solid.js JSX compiles to in a simplified way.