SolidJS is a modern JavaScript library for building web user interfaces, created by Ryan Carniato and released in 2020, which takes a reactive approach to building UIs, which means that the UI updates automatically in response to changes in the underlying data or state.
Some of the key features of SolidJS include:
- A small API and lightweight bundle size, making it fast and easy to use
- Support for server-side rendering, which can improve performance and SEO
- A simple and intuitive syntax, with a focus on declarative programming
- An efficient rendering engine that minimizes unnecessary updates to the DOM
SolidJS vs React
Of course, SolidJS is often compared to other popular JavaScript libraries/frameworks like React, Vue, and Angular. It has gained popularity among developers who are looking for a lightweight and modern alternative to these more established options.
However, there are some notable differences and advantages to using SolidJS over React and other JavaScript frameworks.
First of all: why is it compared to React?
SolidJS is compared to React because both are designed to create UI components and manage the state of web applications.
If you’ve developed with React and its “functional components” before, SolidJS will feel very natural because it follows the same philosophy, with unidirectional data flow, read/write segregation, and immutable interfaces.
They share similar concepts such as component-driven architecture and reactive programming paradigms. However, the way they handle reactivity and updates is different. React relies on a virtual DOM and a diffing algorithm to update the UI, while SolidJS uses fine-grained reactivity and compiles the components down to highly optimized code with minimal overhead.
Since they share a lot of philosophy and both make use of JSX, their code syntax is very similar. For example, we are going to create a simple UserInfo component that takes in three properties: username, role, and points and returns an output based on the user role.
This is the code written in React:
import React from "react";
function UserInfo(props) {
const { username, role, points } = props;
return (
<div>
<p>Welcome {username}! You currently have {points * 1000} points!</p>
{role === "admin" && <div>Admin Panel</div>}
{role === "guest" && <div>Guest Panel</div>}
</div>
);
}
export default UserInfo;
Code language: JavaScript (javascript)
In this component, we destructure the username, role, and points properties from the props object. Then, we use a template literal to produce the welcome message with the username and points values, and finally, we use two conditional rendering statements with the && operator to display the admin or guest panel based on the role property.
The following is the same component written in TypeScript and SolidJS:
import { JSX, createSignal } from "solid-js";
interface Props {
username: string;
role: string;
points: number;
}
export default function UserInfo(props: Props): JSX.Element {
const { username, role, points } = props;
const isAdmin = role === "admin";
const isGuest = role === "guest";
const pointsFormatted = points * 1000;
return (
<div>
<p>Welcome {username}! You currently have {pointsFormatted} points!</p>
{isAdmin && <div>Admin Panel</div>}
{isGuest && <div>Guest Panel</div>}
</div>
);
}
Code language: JavaScript (javascript)
SolidJS does use JSX like React, but it is not mandatory. SolidJS provides a “template syntax” alternative to JSX, which is similar to regular HTML syntax. This alternative syntax can be used instead of JSX in SolidJS components.
Moreover, SolidJS components can be written entirely in TypeScript, without any JSX or template syntax, if desired. However, using either JSX or the template syntax in SolidJS components can make the code more readable and easier to understand, especially for those who are already familiar with HTML and/or JSX.
Here is the previous SolidJS component using the template syntax:
import { createSignal } from "solid-js";
interface Props {
username: string;
role: string;
points: number;
}
export default function UserInfo(props: Props) {
const { username, role, points } = props;
const isAdmin = role === "admin";
const isGuest = role === "guest";
const pointsFormatted = points * 1000;
return `
<div>
<p>Welcome ${username}! You currently have ${pointsFormatted} points!</p>
${isAdmin ? '<div>Admin Panel</div>' : ''}
${isGuest ? '<div>Guest Panel</div>' : ''}
</div>
`;
}
Code language: JavaScript (javascript)
In this example, we use backticks to define a template string that represents the component’s HTML structure. We use ${} to insert dynamic data and expressions into the HTML, just like in regular template literals. We also use ternary operators to conditionally render HTML elements within the template string.
Why is SolidJS a lot more than a React or Svelte alternative?
SolidJS is more than a React alternative because it offers several unique features and advantages.
Fine-grained reactivity
First of all, the previously mentioned fine-grained reactivity. SolidJS employs a fine-grained reactive system that tracks individual changes in the application state, leading to efficient and fast updates.
Moreover, it offers compiler optimizations having a compiler that optimizes the code during build time, resulting in smaller and faster components with minimal runtime overhead.
No virtual DOM: SolidJS eliminates the need for a virtual DOM by using a compile-time approach, thus reducing memory usage and improving performance.
Fine-grained reactivity is a key feature of SolidJS. It means that SolidJS tracks dependencies at a very granular level, down to the individual properties or expressions that are used in a component. This allows SolidJS to update only the parts of the UI that actually need to change, instead of re-rendering the entire component every time there is a change.
This fine-grained reactivity is achieved through a technique called reactive programming.
In SolidJS, reactive programming is built into the core of the framework and is used to automatically update the UI when data changes. This means that developers don’t need to manually write code to update the UI in response to changes in data.
Performance boost
The performance benefits of fine-grained reactivity are noticeable in benchmarks and real-world applications. The SolidJS development team claims that its reactivity engine is faster than other popular frameworks like React and Vue, especially in scenarios where components have a lot of data or complex logic.
However, the SolidJS team also emphasizes that performance is not the only benefit of its reactive programming approach. By automatically updating the UI in response to changes, SolidJS can make development faster and easier, since developers don’t need to write as much code to manage state and UI updates.
This can result in a simpler and more intuitive development experience, which can be especially valuable for complex or large-scale applications.
SolidJS vs Svelte
In this context, SolidJS is often compared to Svelte, another popular yet incredibly fast Javascript library.
SolidJS and Svelte are in fact both considered similar and among the fastest JavaScript frameworks because they both use a reactive programming approach and a compile-time optimization technique to achieve high performance and minimal overhead.
Both SolidJS and Svelte use a compile-time optimization technique to minimize the size of the final application code. SolidJS uses a TypeScript-based compiler that analyzes the code at compile time and generates optimized JavaScript code that removes unnecessary code and improves performance. Svelte, on the other hand, uses a technique called “the Svelte compiler” which analyzes the code and generates highly optimized vanilla JavaScript code.
SolidJS does not use a separate JavaScript compiler. Instead, SolidJS leverages the JavaScript runtime environment directly to interpret and execute its code.
SolidJS is a runtime framework, which means that its code is executed at runtime by the JavaScript engine in the browser or on the server (e.g., using Node.js). SolidJS provides a set of APIs and tools that developers can use to build reactive components and applications using a declarative syntax. The SolidJS code is written in TypeScript or JavaScript, and is compiled by the TypeScript compiler (if using TypeScript) to produce JavaScript code that can be executed by the JavaScript engine.
When a SolidJS application is run, the SolidJS runtime engine processes the components and creates a virtual representation of the UI. The SolidJS runtime then uses this virtual representation to efficiently update the real DOM as changes are made to the application state.
And last but not least, SolidJS provides out-of-the-box support for server-side rendering (SSR), which can improve the initial load time and SEO of your web application.
Signals: more than state variables
In SolidJS, signals are a core concept used to manage reactive state. Signals are similar to state variables in other frameworks like React, but they are more flexible and can represent a wide range of data types, including objects, arrays, and functions.
A signal is essentially a reactive value that can be read and updated by a SolidJS component. When a signal is updated, SolidJS automatically triggers a re-render of the component to reflect the updated value.
In SolidJS, signals are created using the createSignal function. This function takes an initial value as an argument and returns a tuple that contains two elements: the signal value itself, and a function that can be used to update the signal value. Here is an example:
import { createSignal } from "solid-js";
function Counter() {
const [count, setCount] = createSignal(0);
function handleClick() {
setCount(count() + 1);
}
return (
<div>
<p>Count: {count()}</p>
<button onClick={handleClick}>Increment</button>
</div>
);
}
Code language: JavaScript (javascript)
In this example, we create a signal called count using createSignal and initialize it to 0. We then define a handleClick function that updates the count signal by calling setCount with the current count value plus one. Finally, we render the count value and a button that triggers the handleClick function.
As you can see within the code we added the parenthesis when using state variables, like count() instead of count.
This is because SolidJS uses reactive programming and lazy evaluation to efficiently update the UI. When we use a state variable in a SolidJS component, we are actually creating a signal and reading its current value by calling it a function. This approach allows SolidJS to track dependencies and update the UI only when necessary, without the need for manual updates or re-renders. Therefore, when we want to read the current value of a state variable in a SolidJS component, we need to call it like a function using parenthesis ().
Other SolidJS concepts
Other SolidJS powerful concepts which provide a range of tools and techniques for building fast, scalable, and modular applications with SolidJS are the following.
- Fragments, which allow you to group a list of elements together without introducing an additional parent element. This can be useful when you don’t want to add an extra wrapper element to your component tree.
- Portals, provide a way to render a child component at a different position in the DOM hierarchy than its parent component. This can be useful when you need to render a component outside of its parent component for styling or z-index reasons.
- Context, which allows you to pass data down the component tree without the need to pass props through intermediate components. This can be useful when you have a large component tree with many nested components that all need access to the same data.
- Suspense is a technique for deferring the rendering of a component until its data has been fetched or loaded. This can help improve the perceived performance of an application by showing a loading indicator while the data is being fetched.
- Error boundaries allow you to gracefully handle errors that occur within a component. This can help prevent the entire application from crashing due to an error in a single component.
- Lazy components allow you to defer the loading of a component until it is actually needed. This can help improve the initial loading time of an application by loading only the components that are immediately necessary.
- Async and concurrent rendering, allow components to be rendered in parallel, which can improve the overall performance of an application. This can be particularly useful for large or complex components that take a long time to render.
- Implicit delegation allows you to define a set of properties or methods on a parent component that can be automatically delegated to child components. This can help reduce boilerplate code and make components more modular.
- Server-side rendering (SSR) and hydration allow you to render a SolidJS application on the server and then rehydrate it on the client. This can improve the initial loading time of an application and improve SEO.
- Directives, which provide a way to attach behaviors to HTML elements without the need for custom components. This can be useful for adding dynamic behavior to existing HTML elements.
- Streaming allows the server to send data to the client as soon as it is available, without waiting for the entire response to be generated. This can help improve the perceived performance of an application by reducing the initial loading time.