Solid.js Complete Guide | Fast Reactive JavaScript Framework

Solid.js Complete Guide | Fast Reactive JavaScript Framework

이 글의 핵심

Solid.js is a reactive JavaScript framework that compiles to efficient vanilla JavaScript. It uses fine-grained reactivity for exceptional performance without a Virtual DOM.

Introduction

Solid.js is a declarative JavaScript framework for building user interfaces. Unlike React, it compiles to efficient vanilla JavaScript without a Virtual DOM, achieving exceptional performance through fine-grained reactivity.

React vs Solid

React:

function Counter() {
  const [count, setCount] = useState(0);
  console.log('Component re-renders'); // Logs on every update
  
  return <button onClick={() => setCount(count + 1)}>{count}</button>;
}

Solid:

function Counter() {
  const [count, setCount] = createSignal(0);
  console.log('Component runs once'); // Only logs once!
  
  return <button onClick={() => setCount(count() + 1)}>{count()}</button>;
}

1. Installation

# Create new project
npx degit solidjs/templates/ts my-app
cd my-app
npm install
npm run dev

# Or with Vite
npm create vite@latest my-app -- --template solid-ts

2. Reactive Primitives

Signals (State)

import { createSignal } from 'solid-js';

function Counter() {
  const [count, setCount] = createSignal(0);
  
  // Read: count()
  // Write: setCount(value)
  
  return (
    <div>
      <p>Count: {count()}</p>
      <button onClick={() => setCount(count() + 1)}>Increment</button>
      <button onClick={() => setCount(c => c + 1)}>Increment (updater)</button>
    </div>
  );
}

Derived Signals (Computed)

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

function ExpensiveComputation() {
  const [count, setCount] = createSignal(0);
  
  // Memoized computation
  const doubled = createMemo(() => {
    console.log('Computing doubled');
    return count() * 2;
  });
  
  return (
    <div>
      <p>Count: {count()}</p>
      <p>Doubled: {doubled()}</p>
      <button onClick={() => setCount(count() + 1)}>Increment</button>
    </div>
  );
}

Effects

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

function Logger() {
  const [count, setCount] = createSignal(0);
  
  // Runs when dependencies change
  createEffect(() => {
    console.log('Count changed to:', count());
  });
  
  return <button onClick={() => setCount(count() + 1)}>Click</button>;
}

3. Control Flow

Show Component

import { Show } from 'solid-js';

function UserProfile({ userId }: { userId: number | null }) {
  return (
    <Show when={userId} fallback={<div>Please log in</div>}>
      <UserDetails userId={userId!} />
    </Show>
  );
}

For Component

import { For } from 'solid-js';

function TodoList() {
  const [todos, setTodos] = createSignal([
    { id: 1, text: 'Learn Solid' },
    { id: 2, text: 'Build app' },
  ]);
  
  return (
    <ul>
      <For each={todos()}>
        {(todo, index) => (
          <li>
            {index() + 1}. {todo.text}
          </li>
        )}
      </For>
    </ul>
  );
}

Switch Component

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

function StatusBadge({ status }: { status: string }) {
  return (
    <Switch fallback={<span>Unknown</span>}>
      <Match when={status === 'active'}>
        <span className="badge-green">Active</span>
      </Match>
      <Match when={status === 'pending'}>
        <span className="badge-yellow">Pending</span>
      </Match>
      <Match when={status === 'inactive'}>
        <span className="badge-gray">Inactive</span>
      </Match>
    </Switch>
  );
}

4. Components

Props

import { Component } from 'solid-js';

interface ButtonProps {
  onClick: () => void;
  children: any;
  variant?: 'primary' | 'secondary';
}

const Button: Component<ButtonProps> = (props) => {
  return (
    <button
      onClick={props.onClick}
      class={`btn btn-${props.variant || 'primary'}`}
    >
      {props.children}
    </button>
  );
};

Children

import { children } from 'solid-js';

function Container(props: { children: any }) {
  const c = children(() => props.children);
  
  return <div class="container">{c()}</div>;
}

5. Stores (Global State)

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

function TodoApp() {
  const [todos, setTodos] = createStore([
    { id: 1, text: 'Learn Solid', completed: false },
  ]);
  
  const addTodo = (text: string) => {
    setTodos([...todos, { id: Date.now(), text, completed: false }]);
  };
  
  const toggleTodo = (id: number) => {
    setTodos(
      (todo) => todo.id === id,
      'completed',
      (completed) => !completed
    );
  };
  
  return (
    <div>
      <For each={todos}>
        {(todo) => (
          <div>
            <input
              type="checkbox"
              checked={todo.completed}
              onChange={() => toggleTodo(todo.id)}
            />
            {todo.text}
          </div>
        )}
      </For>
    </div>
  );
}

6. Context API

import { createContext, useContext } from 'solid-js';

const UserContext = createContext<{ name: string }>();

function App() {
  const [user] = createSignal({ name: 'John' });
  
  return (
    <UserContext.Provider value={user()}>
      <UserProfile />
    </UserContext.Provider>
  );
}

function UserProfile() {
  const user = useContext(UserContext);
  return <div>{user?.name}</div>;
}

7. Async Data

createResource

import { createResource } from 'solid-js';

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

function UserProfile() {
  const [userId, setUserId] = createSignal(1);
  const [user] = createResource(userId, fetchUser);
  
  return (
    <div>
      <Show when={user.loading}>Loading...</Show>
      <Show when={user.error}>Error: {user.error.message}</Show>
      <Show when={user()}>
        <h1>{user()?.name}</h1>
      </Show>
    </div>
  );
}

8. Routing

npm install @solidjs/router
import { Router, Route, A } from '@solidjs/router';

function App() {
  return (
    <Router>
      <nav>
        <A href="/">Home</A>
        <A href="/about">About</A>
      </nav>
      
      <Route path="/" component={Home} />
      <Route path="/about" component={About} />
      <Route path="/users/:id" component={UserProfile} />
    </Router>
  );
}

9. Performance

Solid.js is incredibly fast:

  • No Virtual DOM - direct DOM manipulation
  • Fine-grained reactivity - only what changed updates
  • Compiled - optimized at build time

Benchmark results (js-framework-benchmark):

  • Solid.js: 1.0x (baseline)
  • React: 1.5x slower
  • Vue: 1.3x slower

Summary

Solid.js delivers exceptional performance:

  • Fine-grained reactivity updates only what changed
  • No Virtual DOM for faster updates
  • Familiar JSX syntax like React
  • Compile-time optimization for smaller bundles
  • Full TypeScript support

Key Takeaways:

  1. Components run once, signals track changes
  2. Use createSignal for state, createMemo for derived
  3. Control flow with <Show>, <For>, <Switch>
  4. createStore for complex nested state
  5. createResource for async data

Next Steps:

Resources: