--- name: solid-development description: SolidJS patterns, reactivity model, and best practices. Use when writing Solid components, reviewing Solid code, or debugging Solid issues. --- # Solid Development Fine-grained reactivity patterns for SolidJS. ## Instructions SolidJS is NOT React. The mental model is fundamentally different: | React | SolidJS | |-------|---------| | Components re-run on state change | Components run **once** | | Virtual DOM diffing | Direct DOM updates | | Hooks with dependency arrays | Automatic dependency tracking | | `useState` returns value | `createSignal` returns getter function | ### 1. Signals — Reactive Primitives Signals are getter/setter pairs that track dependencies automatically: ```jsx import { createSignal } from "solid-js"; function Counter() { const [count, setCount] = createSignal(0); // ^ getter (function!) ^ setter return ( ); } ``` **Rules:** - Always call the getter: `count()` not `count` - The component function runs once — only the reactive parts update - Signals accessed in JSX are automatically tracked ### 2. Effects — Side Effects Effects run when their tracked signals change: ```jsx import { createSignal, createEffect } from "solid-js"; function Logger() { const [count, setCount] = createSignal(0); // ✅ Tracked — runs when count changes createEffect(() => { console.log("Count is:", count()); }); // ❌ NOT tracked — runs once at setup console.log("Initial:", count()); return ; } ``` **Key insight:** Only signals accessed *inside* the effect are tracked. ### 3. Memos — Derived Values Cache expensive computations: ```jsx import { createSignal, createMemo } from "solid-js"; function FilteredList() { const [items, setItems] = createSignal([]); const [filter, setFilter] = createSignal(""); // Only recomputes when items or filter change const filtered = createMemo(() => items().filter(item => item.includes(filter())) ); return {item =>
{item}
}
; } ``` ### 4. Props — Don't Destructure! **Critical:** Destructuring props breaks reactivity. ```jsx // ❌ BROKEN — loses reactivity function Greeting({ name }) { return

Hello {name}

; } // ❌ ALSO BROKEN function Greeting(props) { const { name } = props; return

Hello {name}

; } // ✅ CORRECT — maintains reactivity function Greeting(props) { return

Hello {props.name}

; } ``` **For defaults, use `mergeProps`:** ```jsx import { mergeProps } from "solid-js"; function Button(props) { const merged = mergeProps({ variant: "primary" }, props); return ; } ``` **For splitting props, use `splitProps`:** ```jsx import { splitProps } from "solid-js"; function Input(props) { const [local, inputProps] = splitProps(props, ["label"]); return ( ); } ``` ### 5. Control Flow Components Don't use JS control flow in JSX — use Solid's components: **Conditionals with ``:** ```jsx import { Show } from "solid-js"; }> ``` **Multiple conditions with ``/``:** ```jsx import { Switch, Match } from "solid-js"; Loading... Error! ``` **Lists with ``:** ```jsx import { For } from "solid-js"; {(item, index) =>
  • {index()}: {item.name}
  • }
    ``` **`` vs ``:** | Use | When | |-----|------| | `` | List order/length changes (general case) | | `` | Fixed positions, content changes (performance optimization) | With ``, `item` is a signal: `{(item, i) =>
    {item().name}
    }` ### 6. Stores — Complex State Use stores for nested objects and shared state: ```jsx import { createStore } from "solid-js/store"; function TodoApp() { const [state, setState] = createStore({ todos: [], filter: "all" }); const addTodo = (text) => { setState("todos", todos => [...todos, { text, done: false }]); }; const toggleTodo = (index) => { setState("todos", index, "done", done => !done); }; return (/* ... */); } ``` **When to use:** - Signals: Simple values, local state - Stores: Objects, arrays, shared state, nested data ### 7. Data Fetching with Resources ```jsx import { createResource, Suspense } from "solid-js"; function UserProfile(props) { const [user] = createResource(() => props.userId, fetchUser); return ( }> }> ); } ``` **Resource properties:** - `user()` — the data (or undefined) - `user.loading` — boolean - `user.error` — error if failed - `user.latest` — last successful value ### 8. Context for Shared State ```jsx import { createContext, useContext } from "solid-js"; import { createStore } from "solid-js/store"; const AppContext = createContext(); function AppProvider(props) { const [state, setState] = createStore({ user: null, theme: "light" }); return ( {props.children} ); } function useApp() { return useContext(AppContext); } ``` ## Common Mistakes | Mistake | Problem | Fix | |---------|---------|-----| | `const { name } = props` | Breaks reactivity | Access `props.name` directly | | `count` instead of `count()` | Gets function, not value | Call the signal getter | | `console.log(count())` outside effect | Only runs once | Put in `createEffect` | | Using `.map()` for lists | No keyed updates | Use `` component | | Ternary in JSX for conditionals | Works but less efficient | Use `` component | | Multiple signals for related data | Verbose, hard to manage | Use `createStore` | ## Examples ### Complete Component Pattern ```jsx import { createSignal, createMemo, createEffect, Show, For } from "solid-js"; function TaskList(props) { const [filter, setFilter] = createSignal("all"); // Derived state const filteredTasks = createMemo(() => { const f = filter(); if (f === "all") return props.tasks; return props.tasks.filter(t => (f === "done" ? t.done : !t.done)); }); // Side effect createEffect(() => { console.log(`Showing ${filteredTasks().length} tasks`); }); return (
    0} fallback={

    No tasks

    }>
      {task => (
    • {task.text}
    • )}
    ); } ```