--- name: solidjs description: | SolidJS framework development skill for building reactive web applications with fine-grained reactivity. Use when working with SolidJS projects including: (1) Creating components with signals, stores, and effects, (2) Implementing reactive state management, (3) Using control flow components (Show, For, Switch/Match), (4) Setting up routing with Solid Router, (5) Building full-stack apps with SolidStart, (6) Data fetching with createResource, (7) Context API for shared state, (8) SSR/SSG configuration. Triggers: solid, solidjs, solid-js, solid start, solidstart, createSignal, createStore, createEffect. --- # SolidJS Development SolidJS is a declarative JavaScript library for building user interfaces with fine-grained reactivity. Unlike virtual DOM frameworks, Solid compiles templates to real DOM nodes and updates them with fine-grained reactions. ## Core Principles 1. **Components run once** — Component functions execute only during initialization, not on every update 2. **Fine-grained reactivity** — Only the specific DOM nodes that depend on changed data update 3. **No virtual DOM** — Direct DOM manipulation via compiled templates 4. **Signals are functions** — Access values by calling: `count()` not `count` ## Reactivity Primitives ### Signals — Basic State ```tsx import { createSignal } from "solid-js"; const [count, setCount] = createSignal(0); // Read value (getter) console.log(count()); // 0 // Update value (setter) setCount(1); setCount(prev => prev + 1); // Functional update ``` **Options:** ```tsx const [value, setValue] = createSignal(initialValue, { equals: false, // Always trigger updates, even if value unchanged name: "debugName" // For devtools }); ``` ### Effects — Side Effects ```tsx import { createEffect } from "solid-js"; createEffect(() => { console.log("Count changed:", count()); // Runs after render, re-runs when dependencies change }); ``` **Key behaviors:** - Initial run: after render, before browser paint - Subsequent runs: when tracked dependencies change - Never runs during SSR or hydration - Use `onCleanup` for cleanup logic ### Memos — Derived/Cached Values ```tsx import { createMemo } from "solid-js"; const doubled = createMemo(() => count() * 2); // Access like signal console.log(doubled()); // Cached, only recalculates when count changes ``` Use memos when: - Derived value is expensive to compute - Derived value is accessed multiple times - You want to prevent downstream updates when result unchanged ### Resources — Async Data ```tsx import { createResource } from "solid-js"; const [user, { mutate, refetch }] = createResource(userId, fetchUser); // In JSX }>
{user()?.name}
// Resource properties user.loading // boolean user.error // error if failed user.state // "unresolved" | "pending" | "ready" | "refreshing" | "errored" user.latest // last successful value ``` ## Stores — Complex State For nested objects/arrays with fine-grained updates: ```tsx import { createStore } from "solid-js/store"; const [state, setState] = createStore({ user: { name: "John", age: 30 }, todos: [] }); // Path syntax updates setState("user", "name", "Jane"); setState("todos", todos => [...todos, newTodo]); setState("todos", 0, "completed", true); // Produce for immer-like updates import { produce } from "solid-js/store"; setState(produce(s => { s.user.age++; s.todos.push(newTodo); })); ``` **Store utilities:** - `produce` — Immer-like mutations - `reconcile` — Diff and patch data (for API responses) - `unwrap` — Get raw non-reactive object ## Components ### Basic Component ```tsx import { Component } from "solid-js"; const MyComponent: Component<{ name: string }> = (props) => { return
Hello, {props.name}
; }; ``` ### Props Handling ```tsx import { splitProps, mergeProps } from "solid-js"; // Default props const merged = mergeProps({ size: "medium" }, props); // Split props (for spreading) const [local, others] = splitProps(props, ["class", "onClick"]); return )}> ``` ### Suspense — Async Loading ```tsx import { Suspense } from "solid-js"; }> ``` ## Context API ```tsx import { createContext, useContext } from "solid-js"; // Create context const CounterContext = createContext<{ count: () => number; increment: () => void; }>(); // Provider component export function CounterProvider(props) { const [count, setCount] = createSignal(0); return ( setCount(c => c + 1) }}> {props.children} ); } // Consumer hook export function useCounter() { const ctx = useContext(CounterContext); if (!ctx) throw new Error("useCounter must be used within CounterProvider"); return ctx; } ``` ## Lifecycle ```tsx import { onMount, onCleanup } from "solid-js"; function MyComponent() { onMount(() => { console.log("Mounted"); const handler = () => {}; window.addEventListener("resize", handler); onCleanup(() => { window.removeEventListener("resize", handler); }); }); return
Content
; } ``` ## Refs ```tsx let inputRef: HTMLInputElement; { /* el is the DOM element */ }} /> ``` ## Event Handling ```tsx // Standard events (lowercase) // Delegated events (on:) // Native events (on:) - not delegated
``` ## Common Patterns ### Conditional Classes ```tsx import { clsx } from "clsx"; // or classList
``` ### Batch Updates ```tsx import { batch } from "solid-js"; batch(() => { setName("John"); setAge(30); // Effects run once after batch completes }); ``` ### Untrack ```tsx import { untrack } from "solid-js"; createEffect(() => { console.log(count()); // tracked console.log(untrack(() => other())); // not tracked }); ``` ## TypeScript ```tsx import type { Component, ParentComponent, JSX } from "solid-js"; // Basic component const Button: Component<{ label: string }> = (props) => ( ); // With children const Layout: ParentComponent<{ title: string }> = (props) => (

{props.title}

{props.children}
); // Event handler types const handleClick: JSX.EventHandler = (e) => { console.log(e.currentTarget); }; ``` ## Project Setup ```bash # Create new project npm create solid@latest my-app # With template npx degit solidjs/templates/ts my-app # SolidStart npm create solid@latest my-app -- --template solidstart ``` **vite.config.ts:** ```ts import { defineConfig } from "vite"; import solid from "vite-plugin-solid"; export default defineConfig({ plugins: [solid()] }); ``` ## Anti-Patterns to Avoid 1. **Destructuring props** — Breaks reactivity ```tsx // ❌ Bad const { name } = props; // ✅ Good props.name ``` 2. **Accessing signals outside tracking scope** ```tsx // ❌ Won't update console.log(count()); // ✅ Will update createEffect(() => console.log(count())); ``` 3. **Forgetting to call signal getters** ```tsx // ❌ Passes the function
{count}
// ✅ Passes the value
{count()}
``` 4. **Using array index as key** — Use `` for reference-keyed, `` for index-keyed 5. **Side effects during render** — Use `createEffect` or `onMount`