Skip to content
Logo Theodo

SolidJS for beginners: a story of virtual DOM and signals

Elise RECEJAC7 min read

A cheatsheet of React vs Solid

Signals are everywhere. After some years of React domination where signals were put aside, they are now back! A lot of rising frameworks use it (like Preact or Qwik), and even Angular integrated it in version 16.

I would like to focus on a less-known framework: SolidJS. It first appeared in 2021 in the StateOfJS.

Solid and the galaxy of frameworks: State Of JS 2022

Solid and the galaxy of frameworks: State Of JS 2022

Disclaimer: you’ll need some basics on React to understand this article.

TL;DR

Cheatsheet React vs SolidJS Cheatsheet React vs SolidJS

Learning by doing: an example

I’ve implemented the same feature in both React and Solid (inspired by a great article on signals in Solid: A Hands-on Introduction to Fine-Grained Reactivity).

The feature is the following:

Three buttons to modify a displayed text

And I have made two implementations: one using React and the other using SolidJS.

React implementation:

function App() {
  console.log("Create component");
  const [firstName, setFirstName] = useState("John");
  const [lastName, setLastName] = useState("Smith");
  const [showFullName, setShowFullName] = useState(true);

  const displayName = useMemo(() => {
    console.log("Generate display name");
    return !showFullName ? firstName : `${firstName} ${lastName}`;
  }, [firstName, lastName, showFullName]);

  useEffect(() => console.log("My name is", displayName), [displayName]);

  return (
    <>
      <div>{displayName}</div>
      <button onClick={() => setShowFullName(true)}>Show Full Name</button>
      <button onClick={() => setShowFullName(false)}>
        Don't Show Full Name
      </button>
      <button onClick={() => setLastName("Legend")}>Change Last Name</button>
    </>
  );
}

Solid implementation:

const App: Component = () => {
  console.log("Create component");
  const [firstName, setFirstName] = createSignal("John");
  const [lastName, setLastName] = createSignal("Smith");
  const [showFullName, setShowFullName] = createSignal(true);

  const displayName = () => {
    console.log("Generate display name");
    if (!showFullName()) return firstName();
    return `${firstName()} ${lastName()}`;
  };

  createEffect(() => console.log("My name is", displayName()));

  return (
    <>
      <div>{displayName()}</div>
      <button onClick={() => setShowFullName(true)}>Show Full Name</button>
      <button onClick={() => setShowFullName(false)}>
        Don't Show Full Name
      </button>
      <button onClick={() => setLastName("Legend")}>Change Last Name</button>
    </>
  );
};

First thing you should notice: both implementations are quite similar. Indeed, SolidJS is strongly inspired by React syntax and you can see a lot of similarities between both frameworks.

Main similarities between React and Solid

Main similarities between React and Solid

Both frameworks have a connection to the change management system. Both use a central object to store component data and track their changes: state for React and signal for Solid. Both use a system to re-execute commands when data changes: use/createEffect. And both have a system of memoization: use/createMemo, even if, as you’ll see later, memoization is far less useful in Solid.

So where does SolidJS differ from React?

The main difference between SolidJS and React lies in how the UI is updated to reflect a change in data.

Updates in React

In React, each time a component is mounted or updated, the function render will be called to send the new HTML to the DOM. It shows that each time an update is made, the full component is re-executed.

React component lifecycle

React component lifecycle

Example

For example, for my feature:

Logs

React code

The first logs correspond to the mounting of the components. Then, I interacted several times:

  1. Modification of showFullName state: the component is re-rendered and the useEffect executed since displayName has changed.
  2. Modification of lastName state: the component is re-rendered, displayName doesn’t change (showFullName is false), therefore useEffect isn’t re-executed.
  3. Modification of showFullName state: the component is re-rendered and useEffect is re-executed since displayName has changed.

Hence, each state modification triggers a re-render of the entire component even if the visual has not changed.

Virtual DOM

To re-render components on state update, React uses a virtual DOM. Indeed, the modification of the DOM is really expensive, hence it’s important to modify as few elements as possible, especially since each time an element of the DOM is modified, each of its children must be modified too.

The virtual DOM is used to calculate the optimal way to update the DOM.

When a state is updated in a component, a capture of the DOM is made at the state before the update, and a process called “diffing” computes the difference before and after the update on this projection of the DOM called “virtual DOM”. This process computes the optimal way to update the real DOM.

Virtual DOM and “diffing”

Virtual DOM and “diffing”

On its side, SolidJS doesn’t use a virtual DOM.

Updates in SolidJS

SolidJS uses something called “fine-grained reactivity” to update its DOM.

Fine-grained reactivity

Fine-grained reactivity

The fine-grained reactivity allows Solid to update elements more surgically, recomputing only precise nodes (or functions) using the changed data. It doesn’t need a virtual DOM to update the real DOM efficiently. Solid is even faster than React (you can find more about Solid performance in this article: SolidJS, the solution to build performant Web application!).

Example

If we go back to my feature:

Logs

Solid Code

Again the first logs correspond to the mounting of the component. If I replay the same interactions as in the React example:

  1. Modification of showFullName signal: the component is not fully recomputed but the createEffect is executed since displayName has changed.
  2. Modification of lastName signal: the component is still not recomputed, displayName doesn’t change (showFullName is false), therefore createEffect isn’t re-executed.
  3. Modification of showFullName signal: the component is still not recomputed but createEffect is re-executed since displayName has changed.

So if we compare logs :

React

SolidJS

React

SolidJS

It’s pretty obvious that contrary to React, Solid can manage state changes without recomputing the whole component.

Let’s dive deeper into SolidJS’s fine-grained reactivity mechanism.

Signals

You may have noticed something strange in my implementations. If you’re used to React and its states, you know that you access a state value the same way you would access any variable value:

const [firstName, setFirstName] = useState("John");
const [lastName, setLastName] = useState("Smith");
const [showFullName, setShowFullName] = useState(true);

const displayName = !showFullName ? firstName : `${firstName} ${lastName}`;

However with SolidJS, the signals are not variables, they are functions (you need to add parenthesis to access their values):

const [firstName, setFirstName] = createSignal("John");
const [lastName, setLastName] = createSignal("Smith");
const [showFullName, setShowFullName] = createSignal(true);

const displayName =() => {
  if (!showFullName()) return firstName();
  return `${firstName()} ${lastName()}`;
};

This rather small difference is the key to the entire SolidJS’s fine-grained reactivity.

Signals are based on the Observer design pattern (you can learn more about this pattern in Refactoring Guru’s article).

Each signal comes with a set of subscribers. The function createSignal returns a method to read the signal and a method to edit it.

When you access a signal, for example in a custom function, or a createEffect, it adds this function or this effect to the list of subscribers of the signal.

Then, if you edit the signal’s value, each of its subscribers will be recomputed and updated accordingly.

Signals and reactivity

Signals and reactivity

Key takeaways

SolidJS is a new framework based on React syntax but with a huge difference in its way to synchronize your UI with your data. Where React uses a virtual DOM and recomputes each time the whole component and its children, SolidJS uses signals to make more local updates.

These two methods are very different paradigms and even if you might think that signals are much better (because faster) than virtual DOM it’s also less predictable. If you want to go further into the trade-offs between Solid and React I recommend the React vs Signals: 10 years later article from the creator of SolidJS and his discussion in comments with Dan Abramov (co-creator of Redux and Create React App).

For my part, I would not recommend switching from React to SolidJs in a production code. I think that for most web projects the performance issues are not due to virtual DOM and SolidJS needs a strong understanding of the reactivity to predict the components behaviour. Moreover, it’s a young framework with low documentation so far. However, I’ll definitely follow its growth since reactivity is at the core of rising frontend frameworks.

Liked this article?