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:
- Components run once, signals track changes
- Use
createSignalfor state,createMemofor derived - Control flow with
<Show>,<For>,<Switch> createStorefor complex nested statecreateResourcefor async data
Next Steps:
Resources: