Skills solid-js
install
source · Clone the upstream repo
git clone https://github.com/TerminalSkills/skills
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/TerminalSkills/skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/solid-js" ~/.claude/skills/terminalskills-skills-solid-js && rm -rf "$T"
manifest:
skills/solid-js/SKILL.mdsource content
SolidJS
Overview
SolidJS is a declarative UI library that compiles JSX to real DOM operations — no virtual DOM, no diffing. Reactivity is fine-grained: only the exact DOM nodes that depend on changed data update. The result is near-native performance with a React-like authoring experience.
Installation
# New SolidJS 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
Reactivity Primitives
createSignal — Reactive State
import { createSignal } from "solid-js"; // Signals are getter/setter pairs const [count, setCount] = createSignal(0); // Read: call as function console.log(count()); // 0 // Write: call setter setCount(1); setCount((prev) => prev + 1); // Updater function // Signals with objects const [user, setUser] = createSignal({ name: "Alice", age: 30 }); setUser((prev) => ({ ...prev, age: 31 }));
createEffect — Side Effects
import { createSignal, createEffect } from "solid-js"; const [name, setName] = createSignal("Alice"); // Runs immediately, then reruns whenever dependencies change createEffect(() => { console.log("Name changed:", name()); document.title = `Hello, ${name()}!`; }); // Cleanup function createEffect(() => { const id = setInterval(() => console.log(name()), 1000); return () => clearInterval(id); // Runs before next effect or on dispose });
createMemo — Derived State
import { createSignal, createMemo } from "solid-js"; const [price, setPrice] = createSignal(100); const [quantity, setQuantity] = createSignal(3); // Computed value — only recalculates when dependencies change const total = createMemo(() => price() * quantity()); console.log(total()); // 300 setPrice(120); console.log(total()); // 360 — recalculated
Control Flow
import { Show, For, Switch, Match, Index } from "solid-js"; // Show — conditional rendering <Show when={isLoggedIn()} fallback={<LoginForm />}> <Dashboard /> </Show> // For — list rendering (efficient keyed updates) <For each={items()}> {(item, index) => <div>{index()} - {item.name}</div>} </For> // Index — list rendering when items change in place (not reorder) <Index each={items()}> {(item, index) => <div>{index} - {item().name}</div>} </Index> // Switch/Match — multi-branch conditional <Switch fallback={<p>Unknown status</p>}> <Match when={status() === "loading"}><Spinner /></Match> <Match when={status() === "error"}><ErrorView /></Match> <Match when={status() === "success"}><DataView /></Match> </Switch>
Stores — Complex State
import { createStore, produce } from "solid-js/store"; const [state, setState] = createStore({ users: [] as { id: number; name: string; active: boolean }[], loading: false, }); // Update nested properties setState("loading", true); setState("users", 0, "active", false); // Immer-like mutations with produce setState( produce((s) => { s.users.push({ id: 4, name: "Diana", active: true }); s.loading = false; }) );
Resources — Async Data Fetching
import { createSignal, createResource, Suspense } from "solid-js"; // createResource — wraps async data fetching async function fetchUser(id: number) { const res = await fetch(`/api/users/${id}`); if (!res.ok) throw new Error("User not found"); return res.json(); } function UserProfile() { const [userId, setUserId] = createSignal(1); // Refetches automatically when userId() changes const [user, { refetch, mutate }] = createResource(userId, fetchUser); return ( <Suspense fallback={<p>Loading...</p>}> {/* user() is undefined while loading, the value when ready */} <Show when={user()} fallback={<p>Error loading user</p>}> {(u) => ( <div> <h1>{u().name}</h1> <button onClick={refetch}>Refresh</button> </div> )} </Show> </Suspense> ); }
SolidStart — Full-Stack
SolidStart adds file-based routing, SSR, and server functions:
npm create solid@latest # Choose: SolidStart, TypeScript
File structure:
src/ routes/ index.tsx → / about.tsx → /about users/ index.tsx → /users [id].tsx → /users/:id app.tsx
// src/routes/users/[id].tsx import { createAsync, useParams } from "@solidjs/router"; import { Show, Suspense } from "solid-js"; import { getUser } from "~/lib/users"; // server function export default function UserPage() { const params = useParams(); const user = createAsync(() => getUser(Number(params.id))); return ( <Suspense fallback={<p>Loading...</p>}> <Show when={user()}> {(u) => ( <article> <h1>{u().name}</h1> <p>{u().email}</p> </article> )} </Show> </Suspense> ); }
// src/lib/users.ts — server functions "use server"; export async function getUser(id: number) { const res = await fetch(`https://api.example.com/users/${id}`); return res.json(); } export async function createUser(data: { name: string; email: string }) { // Runs on the server only — safe to access DB return db.insert(users).values(data).returning(); }
Context (Dependency Injection)
import { createContext, useContext, type ParentComponent } from "solid-js"; import { createStore } from "solid-js/store"; const AuthContext = createContext<{ user: () => User | null; login: (u: User) => void }>(); export const AuthProvider: ParentComponent = (props) => { const [state, setState] = createStore<{ user: User | null }>({ user: null }); const value = { user: () => state.user, login: (u: User) => setState("user", u) }; return <AuthContext.Provider value={value}>{props.children}</AuthContext.Provider>; }; export const useAuth = () => useContext(AuthContext)!;
Migrating from React
| React | SolidJS |
|---|---|
| |
| |
| |
| |
| (same API) |
| Not needed — no re-renders |
prop in lists | Use instead of |
| or |
Key differences:
- Components run once — no "re-render" concept. Put reactive logic inside JSX or effects.
- Access signals by calling them —
notcount()
.count - Use
for lists — not<For>
— for efficient keyed rendering..map() - Destructuring signals breaks reactivity — pass signals or use stores.
Guidelines
- Never destructure signals:
breaks reactivity. Useconst { x } = state
orstate.x
.createMemo - Use
instead of<For>
in JSX for efficient list rendering.Array.map - Components run once — initialize logic at the top level, not in callbacks.
- Use
for objects/arrays that need fine-grained updates.createStore - Use
for async data — it integrates withcreateResource
automatically.<Suspense>
is cached — prefer it over callingcreateMemo
to compute derived values.createEffect- SolidStart
functions run exclusively on the server — safe for DB access."use server"