1. javascript
  2. /frameworks
  3. /solid-js

An Introduction to Solid.js

Getting Started

As part of our framework series, we'll now explore the basics of Solid.js. Solid is quickly gaining sympathy and adoption as a JavaScript framework designed for building fast user interfaces with maximum control over reactivity.

Created by Ryan Carniato in 2018, Solid.js is known for its pragmatism and exceptional performance. While it shares similarities with React, such as using components and JSX, it takes a different approach under the hood — eschewing virtual DOM and adopting a compiler like Svelte, which translates our code into efficient vanilla JavaScript.

Unlike React, JSX directly maps to real DOM elements, removing unnecessary abstractions. So, this means that the framework only updates the parts of the DOM that have changed. The result? Incredibly fast and efficient updates.

The framework weighs a mere 7 kilobytes, boasts impressive runtime performance, and requires no special tricks to achieve its speed. Solid.js is truly reactive, featuring a function component that's only called once, allowing for unprecedented control.

Moreover, it has excellent TypeScript support, further enhancing our developer experience.

If you wish to learn or refresh your knowledge on crucial DOM aspects, we suggest you read our Introduction to JavaScript's Document Object Model

Trying Solid.js

There's a lot of novelty involved in web development. So, to ease the experience we strongly suggest trying it online first, in the official Solid playground. If you wish to modify and experiment with the examples you can use a code sandbox where you can easily sign up using your GitHub account.

Before jumping in, we suggest brushing up your JavaScript ES6 syntax

Components and Composition

Our journey into Solid.js begins with understanding the notion of components, a core concept in modern web frameworks. Components allow us to create modular, reusable pieces of UI, aiding in codebase maintainability and promoting a clear separation of concerns.

Creating a Simple Component

Let's start with a basic Solid.js component. Picture that we're developing a website featuring our favorite books, and we begin by creating a Book component to represent each book.

import { createSignal } from "solid-js";

function Book(props) {
  const [likes, setLikes] = createSignal(0);

  function handleClick() {
    setLikes(likes() + 1);
  }

  return (
    <div>
      <h3>{props.title}</h3>
      <p>Author: {props.author}</p>
      <button onClick={handleClick}>Like</button>
      <p>{likes()} likes</p>
    </div>
  );
}

export default Book;

We've created a Book component that takes props as an argument. We use these props to pass data down to the component, like the book's title and author. The component also has a local state for tracking the number of likes, which we manage using the createSignal function imported from the solid-js package.

Basically, we created a reactive signal, that represents the fundamentals of reactivity in Solid. The core primitive createSignal returns a getter and a setter; the first function returns the current value of the signal, while the latter updates the value.

Check out Solid's other primary primitives.

Reactive Signals and Component Updates

One unique feature of Solid.js sets it apart from some other frameworks like React, is that reactive signals can be created outside of a component, which we can utilize to highlight the fine-grained reactivity mechanism Solid.js offers.

However, keep in mind that in a typical application, a state-like "likes" count that is specific to each instance of a component would not be defined outside the component as it would result in all instances sharing the same state. In this case, we're using it as a demonstration to shed light on Solid.js's reactivity system.

Let's update the Book component to reflect this:

import { createSignal } from "solid-js";

// Create a signal outside of any component
const [likes, setLikes] = createSignal(0);

function Book(props) {
  function handleClick() {
    setLikes(likes() + 1);
  }

  console.log(`Number of likes: ${likes()}`);
  
  return (
    <div>
      <h3>{props.title}</h3>
      <p>Author: {props.author}</p>
      <button onClick={handleClick}>Like</button>
      <p>{likes()} likes</p>
    </div>
  );
}

export default Book;

In this updated component, we've moved the likes signal outside and included a console.log statement to inspect the component's re-rendering behavior. Upon clicking the "Like" button to increase the likes count, the console.log statement will be triggered only once. This is indicative of Solid.js's "surgical updates" - the framework only updates the precise parts of the DOM associated with the changed signal rather than re-rendering the entire component, a performance-enhancing feature.

Now that we've taken a slight detour into Solid's fine-grained reactivity and surgical DOM updates, let's pivot our focus back to building more complex UIs through component composition.

In Solid.js, you can craft intricate UI structures by combining smaller, reusable components. This is comparable to assembling a structure using building blocks, where each block is a standalone component.

Composing Components

Let's use our Book component to build a BookList component, which will display a collection of books.

import Book from "./Book";

function BookList() {
  const books = [
    { title: "The Catcher in the Rye", author: "J.D. Salinger" },
    { title: "To Kill a Mockingbird", author: "Harper Lee" },
    { title: "1984", author: "George Orwell" }
  ];

  return (
    <div>
      <h2>My Favorite Books</h2>
      {books.map((book) => (
        <Book title={book.title} author={book.author} />
      ))}
    </div>
  );
}

export default BookList;

In this BookList component, we are importing the Book component, and using it inside the map function to render each book. Each item in the books array is passed as props to the Book component. This results in a list of books, each presented as an instance of the Book component.

You might have noticed the {...book} syntax inside the Book component tag. This is called the spread operator in JavaScript, and it's a convenient way to pass all the properties of an object as props to a component.

So, {...book} is equivalent to writing <Book id={book.id} title={book.title} author={book.author} />. In both cases, the Book component receives id, title, and author as props. This way, if we were to add more fields to our book objects in the future, they would automatically be included as props without needing to modify this line of code.

The key prop, which gets its value from book.id, is a special prop in Solid.js (and many other libraries like React) that helps the framework uniquely identify each instance of a component in a list. Providing a key prop can boost performance and ensure consistent behavior when the list is updated.

JSX and Templating

Solid.js leverages JSX, a templating syntax that allows you to write HTML-like code in your JavaScript. Why is this relevant to our exploration of Solid.js? Because JSX isn't just for defining the structure of our components - it's also used to express dynamic content and behaviors, which are then managed by Solid.js or any other library or framework used with JSX.

How Solid.js Uses JSX

JSX forms the backbone of our component structures. It's also instrumental in rendering dynamic content and handling user interactions. For instance, let's dissect the JSX in our Book component:

return (
  <div>
    <h3>{props.title}</h3>
    <p>Author: {props.author}</p>
    <button onClick={handleClick}>Like</button>
    <p>{likes()} likes</p>
  </div>
);

Here's what's happening in this block:

  • Dynamic Content: The curly braces {} serve as placeholders for JavaScript expressions. They're used to inject the book's title and author into the h3 and p elements, respectively, and to display the current number of likes. These expressions are reactive and will automatically update in the DOM when the underlying state changes, thanks to Solid.js's reactivity system.

  • Event Handling: JSX handles user interactions via event handling attributes, like onClick. Here, it's used to trigger the handleClick function, increasing the likes count when the button is clicked.

Testing Your Components on CodeSandbox

To experience the behavior of these components live, an online code editor like CodeSandbox is a great platform. Once you log in, simply choose New Sandbox from the Dashboard options, and opt for the Solid Js template, which you'll probably need to search manually. Then, you can structure your files as follows:

/src
  /components
    Book.jsx
    BookList.jsx
  index.jsx

Ensure to paste each component code in its respective file — Book.jsx and BookList.jsx, and the render logic in index.jsx. For rendering your BookList component, use the render function from Solid.js:

import { render } from "solid-js/web";
import BookList from "./components/BookList";

render(() => <BookList />, document.getElementById("app"));

This targets a root element with the id "app".

Apart from the .jsx files for your components, there are a few other files that you'll typically see in this template of a new CodeSandbox project:

  • index.html: This is the HTML file that is loaded when your app runs. It contains a div with an id of "app" where your Solid.js app is rendered.

  • .babelrc: This file configures Babel, a JavaScript compiler that allows you to use next-generation JavaScript today. It includes presets for Solid.js to handle JSX.

  • package.json: This file contains the list of project dependencies and their versions, as well as scripts that can be run on the project.

  • tsconfig.json: This is the configuration file for TypeScript, which we already mentioned. It's a typed superset of JavaScript that compiles to plain JavaScript. Even if you're not using TypeScript, CodeSandbox includes this by default.

These files are typically provided as defaults from CodeSandbox, and you'll rarely need to alter them for basic Solid.js projects. However, having a general understanding of what they do is useful as you dive deeper into JavaScript development.

Solid.js in Compared to Other Frameworks and Libraries

Moving forward, we'll provide a brief comparative insight into how Solid.js aligns or differs from other notable frameworks and libraries in the ecosystem. It's crucial to note that this distinction often blurs, and the terminology is sometimes interchangeable.

React

Solid.js shares a philosophical lineage with React, especially regarding unidirectional data flow and usage of a render library model. However, the fundamental difference lies in their operational mechanics. While React employs a Virtual DOM, Solid.js bypasses this layer, opting for a more direct rendering approach, which could lead to more efficient rendering in certain scenarios.

Vue

Vue and Solid.js both utilize Proxies in their reactive systems, yet diverge when it comes to their rendering paradigms. While Vue's fine-grained reactivity feeds into a less fine-grained Virtual DOM and Component system, Solid.js maintains its granularity down to direct DOM updates, potentially offering more predictable and optimized rendering behavior.

Svelte

Svelte and Solid.js resonate in their pursuit of reactivity and compact execution code bundles. However, their approaches to achieving these goals differ. Solid.js requires more explicit declarations, which, while demanding a tad more from the developer, yields superior performance, especially in larger applications.

For a deeper dive into Solid.js comparisons and migration advice, visit the official comparison page.

When working on a project, choosing a framework or library could significantly impact the development experience and the project's success. It's advisable to weigh the pros and cons, considering factors like the project's scale, the learning curve, and the community support around these technologies.

Engaging with communities, seeking advice, and staying open to changing tools as requirements evolve can lead to more informed decisions. Diving into discussions on platforms like the Solid.js Discord can provide varied perspectives and help you make a well-rounded choice.

Final Thoughts

We've introduced the basics, but Solid also comprises certain distinctive elements. Some noteworthy aspects include:

  • Its approach to component lifecycles offers a different level of control compared to other choices.

  • The automatic tracking of dependencies, which is part of its reactivity model.

  • A focus on native web standards, with provision for custom elements and web components. These features offer an interesting alternative to other front-end libraries and frameworks.

As you explore further, there's a considerable number of resources available to broaden your understanding and enhance your Solid skills. You're encouraged to examine the curated resources below and delve into the FAQs provided on the official Solid website.

Useful Resources

Official Solid.js Offical Website

Solid.js GitHub Repository

Solid.js Community Discord

Ryan Carniato's Blog

Introduction to React

An Introduction to Next.js

Introduction to Svelte - The Lightweight JavaScript Framework

JS Framework Benchmark