---
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}
)}
);
}
```