DEV Community

Alex Spinov
Alex Spinov

Posted on

SolidJS Signals Are the Fastest Reactive Primitives — Here's the Complete Guide

React re-renders components. Solid updates the exact DOM nodes that changed. No virtual DOM, no reconciliation — just surgical updates.

Why SolidJS Signals?

Solid's reactivity system inspired Angular Signals, Svelte 5 Runes, and Vue's Composition API. It is the original fine-grained reactivity for JavaScript.

Core Primitives

createSignal

import { createSignal } from 'solid-js';

function Counter() {
  const [count, setCount] = createSignal(0);

  return (
    <div>
      <p>Count: {count()}</p>
      <button onClick={() => setCount(c => c + 1)}>+</button>
      <button onClick={() => setCount(0)}>Reset</button>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Notice: count() — signals are functions. The component function runs ONCE. Only the {count()} expression re-evaluates.

createEffect

import { createSignal, createEffect } from 'solid-js';

function App() {
  const [name, setName] = createSignal('Alice');

  createEffect(() => {
    console.log('Name changed:', name());
    document.title = `Hello, ${name()}`;
  });

  return <input value={name()} onInput={e => setName(e.target.value)} />;
}
Enter fullscreen mode Exit fullscreen mode

createMemo (Computed)

import { createSignal, createMemo } from 'solid-js';

function App() {
  const [items, setItems] = createSignal([1, 2, 3, 4, 5]);
  const [filter, setFilter] = createSignal(0);

  const filtered = createMemo(() => items().filter(i => i > filter()));
  const total = createMemo(() => filtered().reduce((a, b) => a + b, 0));

  return (
    <div>
      <p>Filtered: {filtered().join(', ')}</p>
      <p>Total: {total()}</p>
      <input type="number" value={filter()} onInput={e => setFilter(+e.target.value)} />
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Stores (Deep Reactivity)

import { createStore, produce } from 'solid-js/store';

function TodoApp() {
  const [state, setState] = createStore({
    todos: [
      { id: 1, text: 'Learn Solid', done: false },
      { id: 2, text: 'Build app', done: false },
    ],
    filter: 'all',
  });

  const toggle = (id: number) => {
    setState(produce(s => {
      const todo = s.todos.find(t => t.id === id);
      if (todo) todo.done = !todo.done;
    }));
  };

  const addTodo = (text: string) => {
    setState('todos', todos => [...todos, { id: Date.now(), text, done: false }]);
  };

  return (
    <ul>
      <For each={state.todos}>
        {(todo) => (
          <li onClick={() => toggle(todo.id)}>
            {todo.done ? '(done) ' : ''}{todo.text}
          </li>
        )}
      </For>
    </ul>
  );
}
Enter fullscreen mode Exit fullscreen mode

Control Flow Components

import { Show, For, Switch, Match } from 'solid-js';

function App() {
  const [user, setUser] = createSignal(null);
  const [items] = createSignal(['a', 'b', 'c']);
  const [status] = createSignal('loading');

  return (
    <div>
      <Show when={user()} fallback={<p>Not logged in</p>}>
        {(u) => <p>Hello, {u().name}</p>}
      </Show>

      <For each={items()}>
        {(item, index) => <p>{index()}: {item}</p>}
      </For>

      <Switch>
        <Match when={status() === 'loading'}>Loading...</Match>
        <Match when={status() === 'error'}>Error!</Match>
        <Match when={status() === 'success'}>Done!</Match>
      </Switch>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Resources (Async Data)

import { createResource, Suspense } from 'solid-js';

const fetchUser = async (id: string) => {
  const res = await fetch(`/api/users/${id}`);
  return res.json();
};

function UserProfile(props: { id: string }) {
  const [user] = createResource(() => props.id, fetchUser);

  return (
    <Suspense fallback={<p>Loading...</p>}>
      <Show when={user()}>
        {(u) => <h2>{u().name}</h2>}
      </Show>
    </Suspense>
  );
}
Enter fullscreen mode Exit fullscreen mode

SolidJS vs React

Feature SolidJS React
Reactivity Fine-grained signals Re-render entire component
Virtual DOM No Yes
Component runs Once Every update
Bundle Size 7KB 40KB+
Speed Fastest Good
Hooks Rules None Many rules

Need reactive data feeds? Check out my Apify actors — real-time data extraction for reactive UIs. For custom solutions, email spinov001@gmail.com.

Top comments (0)