In recent years, a new wave of frameworks has emerged that seek to compete with those already known in web development, such as Angular, React and Vue. Among these new frameworks are Astro and Qwik , which I have talked about in another article. However, in this article we will focus on SolidJS , a framework that has gained notable popularity among software development services looking for a lighter and more modern alternative to established options.
1. What is SolidJS?
It is an open source JavaScript library created by Ryan Carniato, and first released in 2020. It focuses on performance and scalability. It is designed to help developers create fast, efficient and reusable web applications. It combines the syntax and development experience of React with the performance of Frameworks like Svelte. It allows custom software development company to build UIs with tools to enhance their app components with reactivity, which is declarative JavaScript code that links the user interface to the data it creates and uses. It is built on established tools like JSX and TypeScript , and is integrated with the Vite ecosystem .
In SolidJS, components are only executed once, which is when they are rendered for the first time, in the case of hooks and bindings, only when their dependencies are updated.
Another key point of SolidJS is that instead of using a Virtual DOM , it compiles its templates to real DOM nodes and updates them with precise reactivity . The SolidJS code compiler transforms JSX into functions that create and update DOM nodes based on reactive state.
2. Features
SolidJS may seem like an alternative to React, but it is more than that, it has a series of unique features and advantages.
Accurate reactivity
SolidJS makes use of precise reactivity . It uses this system to track individual changes in application state at a very precise level, leading to fast and efficient updates.
This allows SolidJS to update only the parts of the UI that really need a change, without the need to re-render the entire component every time a change occurs to it, as is the case with React.
This reactivity is achieved thanks to a technique called reactive programming , which has been part of the core of the Framework since the beginning. It is used automatically to update the UI every time the data changes, this helps reduce development time, since developers will not have to create extra code to manage the data update.
In addition to that, it introduces improvements to the compiler, and has a compiler that is capable of optimizing the code during the page construction process, thereby achieving smaller and much faster components in a minimum execution time.
Unlike React, it does not use a Virtual DOM. SolidJS eliminates the need for it by following a compile-time execution approach, reducing memory usage and improving performance.
Performance improvements
The benefits of precise reactivity on performance are evident in benchmarks and existing web applications. The SolidJS development team explains that the reactivity engine is much faster than other popular frameworks such as React or Vue, especially in scenarios where components have a large amount of data and logic.
However, the SolidJS team also comments that performance is not the only benefit of reactive programming. By automatically updating the UI based on changes, SolidJS makes development faster and easier as developers will no longer need to write code to manage state changes and updates to the UI.
3. Reactivity
The first thing we should do would be to know the concept of reactivity, which is the ability to react and respond to changes in the data or state of the application. For example, in the case of React, instead of having to manually manipulate DOM elements to reflect changes, it is responsible for re-rendering the component that contains the data when it changes.
In JavaScript, reactivity is not a built-in feature, meaning that when the state changes, the associated logic and view are not automatically updated. It is the task of the Framework or the developer to ensure synchronization between the model and the view.
What SolidJS proposes in this case is to render the component only once, the data or state changes are controlled by the SolidJS reactive primitives , which return a getter and a setter. What will happen to the component below is that it will observe the data of this primitive and in case it changes, it will only be modified where it is located in the DOM, instead of rendering the entire component again, as happens with React.
SolidJS Reactive primitives
This precise reactivity of SolidJS is achieved thanks to the primitives that it offers us, which would be the following:
Signals
Signals are the most important primitive within the SolidJS reactive system. These contain a value, and getter and setter functions, which allow us to know when they are read or modified.
The value they contain changes. And as soon as the value of this signal is updated, all the elements that depend on it will be updated as well.
const [count, setCount] = createSignal(0);
They could be considered event emitters that have a subscription list. And they notify their subscribers every time its value changes.
The interesting thing is how these subscriptions happen. SolidJS makes use of automatic dependency tracking, where updates occur every time data changes.
The trick is in the global stack that exists at compile time. Every time a function that depends on the value of a signal is executed (or re-executed), this function is added to that stack. Each signal that is read checks if a listener exists in this stack and if so, adds it to the subscription list.
Effects
The effects are functions that wrap the readings of the signals and that are re-executed every time the signal on which it is dependent updates its value.
The signals are trackable values, and to complement these, observers are necessary, which will be updated based on those values. An effect is the observer, which executes a side effect that depends on the signal itself.
An important point is that these signals can carry any type of data and effects can do anything with them.
Another point would be that updates occur synchronously. Before the next instruction can be registered, the effect has already been executed.
createEffect(() => console.log(“The latest count is”, count()));
Most of the behavior in SolidJS is built with just these two primitives we’ve mentioned. However, there is another basic primitive we need to talk about.
Memos
Before explaining the following primitive, we should talk about another concept in SolidJS, which is that of derived signals , which are called functions that wrap a signal, such as this one.
const [count, setCount] = createSignal(0);
const doubleCount = () => count() * 2;
This function that wraps a signal is called a derived signal , and by wrapping a signal, it becomes a signal as well, so every time the value of the signal it wraps is updated, the result of this function will also be updated .
There are times when we want to use our data in different ways using the same signal and we end up calling the same derived signal with the same data multiple times. Normally there would be no problem in calling it several times, the fact is that there may be situations in which calling our derived signal repeatedly can have a high performance cost. So we might be interested in caching the result of that function so we don’t have to run it again and reduce duplicate work, unless the dependencies within it change.
For this we use the memos functions, the last type of primitive, which would be cached derived values. These share the particularities of signals and effects. They keep track of the signals they depend on, and only re-execute when they change. By themselves, they would also be signal trackable.
Memos are both an observer and a read-only signal. Since they are aware of both their dependencies and their observers, they can ensure that they are only executed once for each change. This makes them more preferable when recording effects for writing signals.
const doubleCount = createMemo(() => count() * 2);
4. Comparison with other Frameworks
In this section we will make a comparison with the two Frameworks with which SolidJS is most related due to their common points, React and Svelte.
React
Although SolidJS largely aligns with React’s design philosophy, it works fundamentally differently. In this section we will see some of the differences between them.
Absence of a Virtual DOM
One of the most notable differences between SolidJS and React is the absence of a virtual DOM. There is a notion that using the core DOM slows down our applications, and using it is part of what makes SolidJS so performant.
The use of Virtual DOM is a solution that many libraries and Frameworks, such as React, Vue.js and Riot.js, use. However, today, the creators of Svelte and SolidJS have described the Virtual DOM as an extra that only slows down the performance of the library. And they have looked for alternative options that are faster, and these make use of the real DOM.
The way SolidJS has managed to achieve such high speeds without using a Virtual DOM is by compiling its templates on real DOM nodes and controlling the updates that occur within them with precise reactivity. This way, when the application state is updated, only the code that depends on it is executed.
Components are not re-rendered
In Solid, unlike React, components are rendered only once. The JSX expressions and primitives that are used in the component are the ones that are updated.
This provides performance to SolidJS, since it is not necessary to re-render a component every time it is used. SolidJS is reactive enough to be able to track changes that occur within the component itself.
Detailed reactivity
React itself is not completely “reactive”, this is something recognized by the React team .
Svelte
SolidJS is often compared to Svelte, another popular incredibly fast Javascript library.
In fact, both SolidJS and Svelte are considered similar and among the fastest JavaScript frameworks as they both use a reactive programming approach and compile-time optimization technique to achieve high performance and minimal overhead.
Both SolidJS and Svelte use a compile-time optimization technique to minimize the code size of the final application. SolidJS uses a TypeScript-based compiler that analyzes code at compile time and generates optimized JavaScript code, eliminating unnecessary code and improving performance. Svelte, on the other hand, uses a technique called “Svelte compiler” that analyzes the code and generates highly optimized vanilla JavaScript code.
SolidJS does not use a separate JavaScript compiler. Instead, it directly takes advantage of the JavaScript runtime environment to interpret and execute your code.
SolidJS is a Framework where your code is executed at runtime through the JavaScript engine in the browser or on the server. It provides a set of APIs and tools that custom software and mobile app development services can use to build reactive components and applications using declarative syntax. SolidJS code is written in TypeScript or JavaScript, and compiled with the TypeScript compiler, if used, to produce JavaScript code that can be executed by the TypeScript engine.
Last but not least, SolidJS provides built-in support for server-side rendering (SSR), which can improve the initial load time and SEO of your web application.
5. Practical test
For the practical part of this application it has been decided to create a basic application, to see how to work with the Framework from a basic level using the primitives it offers.
The first thing we will have to do is execute the command to generate our SolidJS application, we will need npm to be able to execute the command.
npx degit solidjs/templates/ts
Once the application is created we will have the following folder structure.
We’ll work directly in the App.tsx folder to create our Todo app, since this is the main folder. One of the first things we can see is that the Component creation process is very similar to react, creating the App component as a function.
const App: Component = () =>; {}
signal
The first thing we will do for our Todo application will be create a signal to save the task data.
const [todos, setTodos] = createSignal([]);
This process is very similar to React, where we define a useState to save the data, and receive a tuple of getter and setter, although they are similar, behind them they work differently.
The next thing we are going to do is create a function to add tasks.
const addTodo = (text: string) => {
setTodos([…todos(), { id: ++todoId, text }]);
}
Then we will write the HTML code to paint our task list.
effects
Now we will use another of the primitives that SolidJS offers us, we are going to add an effect that shows us the number of tasks we have at this moment.
createEffect(() =>; {
console.log( `${todos().length} tasks`)
})
We have created an effect, the process is similar to React, but it differs in that we will not have to write an array of dependencies. SolidJS detects all the signals on which the state depends and will execute itself based on the changes in them.
Memos
The last primitive that I am going to try is the memos functions, to show how they work I am going to create a button that is responsible for filtering the tasks that have not been completed, to filter tasks, I have created the following function.
const filterUnfinished = () => {
console.log(‘showing unfinished tasks…’)
return todos().filter(x => !x.finished)
})
I have added a console.log inside the function to see how many times we enter it.
If we click on the button several times, we will see that there is a call to the function for each click we make, even though the number of tasks has not changed, which is what this derived signal depends on. At this point it is interesting to make use of the memos functions, since by using them we can cache the result of the function.
const filterUnfinished = createMemo(() => {
console.log(‘showing unfinished tasks…’)
return todos().filter(x => !x.finished)
})
And so, until its dependencies are updated, the function will not be executed again. The memos are aware of their dependencies and their observers, this makes them able to control that they are only executed once for each change.
6. Conclusions
It is a declarative, efficient and flexible JavaScript library that offers an alternative to Virtual DOM-based frameworks, such as React or Vue. It uses a precise reactivity system that allows the real DOM to be updated selectively and quickly, without the need to render the entire component. It also offers modern features like JSX, Context, Portals, Suspense, SSR, progressive hydration and concurrent rendering.
However, SolidJS also has a high learning curve as it introduces new concepts and requires a new way of thinking for developers, making it a framework best suited for developers who have some experience.
Furthermore, SolidJS is a relatively new project and does not have an ecosystem as large as that of its competitors that have been on the market for longer, so the documentation is not going to be the most complete and it will not have a great number of tools or plugins. Even so, the community is giving it a lot of support and it is growing rapidly and I don’t think it will be a problem that lasts long.
In conclusion, I consider SolidJS to be a library that offers numerous options for building large-scale, highly optimized modern web applications. I believe that, along with Qwik and other libraries that are gaining popularity today, it has the potential to establish itself as a prominent option in the field of web development.