What SolidJS Does
- Fine-Grained Reactivity: Updates exact DOM nodes instead of re-rendering components
- No Virtual DOM: Compiled templates, direct DOM manipulation
- React-like JSX: Familiar syntax for React developers
- Signals: Primitive reactive state management built-in
- SSR & SSG: Server-side rendering and static generation via Solid Start
- Small Bundle: Core library is ~7KB gzipped
- TypeScript First: Excellent type inference
- Progressive: Use as a library or full meta-framework
- Stores: Built-in nested reactive stores
- Context: Dependency injection without re-renders
Architecture Difference
React:
Component re-renders → Virtual DOM diff → DOM update
(Whole component function runs on every change)
Solid:
Signal changes → Specific DOM node updates
(Component function runs ONCE, only reactive parts update)This means in Solid, your component code runs once at mount, and only the specific reactive expressions update on state changes.
Basic Example
Counter
import { createSignal } from "solid-js";
function Counter() {
const [count, setCount] = createSignal(0);
// This console.log runs ONCE (at mount), not on every re-render
console.log("Counter component setup");
return (
<div>
<h1>Count: {count()}</h1>
<button onClick={() => setCount(count() + 1)}>
Increment
</button>
</div>
);
}Note: count() is called as a function (it's a getter). This is what enables Solid to track dependencies.
Derived State
import { createSignal, createMemo } from "solid-js";
function PriceCalculator() {
const [price, setPrice] = createSignal(100);
const [quantity, setQuantity] = createSignal(1);
// Memo only recomputes when price or quantity changes
const total = createMemo(() => price() * quantity());
return (
<div>
<input
type="number"
value={price()}
onInput={(e) => setPrice(+e.currentTarget.value)}
/>
<input
type="number"
value={quantity()}
onInput={(e) => setQuantity(+e.currentTarget.value)}
/>
<p>Total: ${total()}</p>
</div>
);
}Effects
import { createSignal, createEffect } from "solid-js";
function AutoSave() {
const [text, setText] = createSignal("");
createEffect(() => {
// Runs whenever text() changes
localStorage.setItem("draft", text());
console.log("Saved:", text());
});
return (
<textarea
value={text()}
onInput={(e) => setText(e.currentTarget.value)}
/>
);
}Control Flow Components
import { For, Show, Switch, Match } from "solid-js";
function UserList(props) {
return (
<div>
{/* Conditional rendering - more efficient than && */}
<Show when={props.users.length > 0} fallback={<p>No users</p>}>
{/* Keyed list rendering */}
<For each={props.users}>
{(user) => (
<div>
<h3>{user.name}</h3>
<Switch fallback={<p>Unknown status</p>}>
<Match when={user.status === "active"}>
<span class="green">Active</span>
</Match>
<Match when={user.status === "pending"}>
<span class="yellow">Pending</span>
</Match>
</Switch>
</div>
)}
</For>
</Show>
</div>
);
}Stores (Nested Reactivity)
import { createStore } from "solid-js/store";
function TodoApp() {
const [state, setState] = createStore({
todos: [],
filter: "all",
user: { name: "Anonymous", preferences: { theme: "light" } },
});
// Add todo - reactive update
const addTodo = (text) => {
setState("todos", state.todos.length, { id: Date.now(), text, done: false });
};
// Update nested property - only affects dependent components
const toggleTheme = () => {
setState("user", "preferences", "theme", (t) => t === "light" ? "dark" : "light");
};
// Filter todos - reactive
const filteredTodos = () => {
if (state.filter === "all") return state.todos;
if (state.filter === "active") return state.todos.filter(t => !t.done);
return state.todos.filter(t => t.done);
};
return (
<div class={state.user.preferences.theme}>
<button onClick={toggleTheme}>Toggle Theme</button>
<For each={filteredTodos()}>
{(todo) => <div>{todo.text}</div>}
</For>
</div>
);
}Solid Start (Meta Framework)
npm create solid@latest my-appFeatures:
- File-based routing: Like Next.js/Remix
- Server functions: RPC-like server actions
- SSR/SSG/CSR: Flexible rendering modes
- Middleware: Request/response interceptors
- Deployment: Vercel, Netlify, Cloudflare, Node
// routes/posts/[id].tsx
import { RouteDataArgs, useRouteData } from "@solidjs/router";
import { createServerData$ } from "solid-start/server";
export function routeData({ params }: RouteDataArgs) {
return createServerData$(async ([id]) => {
const post = await db.post.findUnique({ where: { id } });
return post;
}, { key: () => [params.id] });
}
export default function Post() {
const post = useRouteData<typeof routeData>();
return (
<article>
<h1>{post()?.title}</h1>
<div innerHTML={post()?.content} />
</article>
);
}Solid vs React vs Svelte
| Feature | SolidJS | React | Svelte | Vue |
|---|---|---|---|---|
| Reactivity | Signals (fine-grained) | Virtual DOM | Compiled | Proxies |
| Bundle size | ~7KB | ~45KB | ~2KB | ~34KB |
| JS Framework Benchmark | Top 3 | Middle | Top 5 | Middle |
| JSX | Yes | Yes | No | Templates |
| Re-renders | Component once | On every change | Compiled | Reactive |
| Learning curve | Easy (if React) | Easy | Easy | Medium |
| Ecosystem | Growing | Massive | Growing | Large |
| TypeScript | Excellent | Good | Good | Excellent |
Migration from React
Solid API is similar to React hooks:
// React
const [count, setCount] = useState(0);
useEffect(() => { console.log(count); }, [count]);
return <div>{count}</div>;
// Solid
const [count, setCount] = createSignal(0);
createEffect(() => { console.log(count()); });
return <div>{count()}</div>; // Note: count() as function callKey differences:
- Signals are getter functions:
count()notcount - No dependency arrays needed (automatic tracking)
- Components don't re-render on state changes
FAQ
Q: Why is Solid faster than React? A: React re-runs the entire component function on every state change and diffs the virtual DOM to find what changed. Solid compiles templates into direct DOM operations and updates specific DOM nodes only when signals change, skipping all virtual-DOM overhead.
Q: Can I use React libraries? A: Not directly (the APIs are different). But Solid has its own ecosystem: Solid Router, Solid Query (similar to TanStack Query), and Solid UI component libraries. Most popular libraries have a Solid version.
Q: Is it suitable for large projects? A: Yes. Solid is already used in production by many companies. TypeScript support is excellent, and performance makes it especially well-suited to performance-sensitive scenarios (dashboards, data visualization, realtime apps). The ecosystem is smaller than React's but complete enough.
Sources & Credits
- GitHub: solidjs/solid — 35.4K+ ⭐ | MIT
- Official site: solidjs.com