# Solid Errors Reactivity Debugging

> >

- **Type:** Skill
- **Install:** `agentstack add skill-impertio-studio-solidjs-claude-skill-package-solid-errors-reactivity-debugging`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [Impertio-Studio](https://agentstack.voostack.com/s/impertio-studio)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [Impertio-Studio](https://github.com/Impertio-Studio)
- **Source:** https://github.com/Impertio-Studio/SolidJS-Claude-Skill-Package/tree/main/skills/source/solid-errors/solid-errors-reactivity-debugging

## Install

```sh
agentstack add skill-impertio-studio-solidjs-claude-skill-package-solid-errors-reactivity-debugging
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# solid-errors-reactivity-debugging

## Quick Reference

### Diagnostic Table: Symptom to Cause to Fix

| Symptom | Cause | Fix |
|---------|-------|-----|
| UI never updates after signal change | Signal getter not called in JSX: `{count}` instead of `{count()}` | ALWAYS call the getter: `{count()}` |
| Effect runs once, never again | Signal accessed outside tracking scope (component body, event handler) | Move signal access inside `createEffect` or JSX expression |
| Effect ignores some signals | Conditional access / early return before signal read | Read ALL signals before any conditional logic |
| Store property not updating in UI | Destructured store property: `const { name } = store` | ALWAYS access via `store.name` in JSX |
| Store update has no effect | Direct mutation: `store.name = "x"` | ALWAYS use `setStore("name", "x")` |
| Props stop updating | Destructured props: `function Comp({ name })` | ALWAYS use `props.name`, use `splitProps` if needed |
| Value is stale in timeout/callback | Signal value captured: `const v = count()` then used later | Call `count()` at the point where you need the value |
| Effect works initially but stops | `await` inside effect breaks synchronous tracking | Move async code to a separate function; read signals before `await` |
| Memo returns stale value | Memo dependency was untracked or destructured | Verify all signal getters are called inside memo callback |
| Store array update not reflected | Used `store.items.push()` instead of setter | Use `setStore("items", items.length, newItem)` or `produce` |

### Critical Warnings

**NEVER** destructure props in the function signature — this reads values once and permanently breaks tracking. ALWAYS accept `props` as a single object and access properties reactively.

**NEVER** store a signal getter result in a variable and expect it to stay current — `const v = count()` captures a snapshot. ALWAYS call the getter where you need the live value.

**NEVER** place `await` before signal reads in an effect — `await` breaks the synchronous tracking scope. ALWAYS read all tracked signals before any `await`.

**NEVER** mutate a store directly — `store.x = 5` bypasses the reactive system entirely. ALWAYS use `setStore` or `produce`.

**NEVER** use `Array.push/splice/pop` on store arrays — these mutate without notifying the reactive graph. ALWAYS use `setStore` with path syntax or `produce`.

---

## Diagnostic Flowchart: "My UI Is Not Updating"

```
START: UI is not updating after state change
  |
  +--> Is the value a signal?
  |     |
  |     +--> Are you calling the getter? count() not count
  |     |     |
  |     |     +--> NO --> FIX: Add () to signal reads in JSX
  |     |     +--> YES --> Is the signal read inside a tracking scope?
  |     |                   |
  |     |                   +--> NO (component body, event handler, onMount)
  |     |                   |     --> FIX: Move read into createEffect or JSX expression
  |     |                   |
  |     |                   +--> YES --> Is there an early return or condition BEFORE the read?
  |     |                         |
  |     |                         +--> YES --> FIX: Read all signals before conditionals
  |     |                         +--> NO --> Is there an await before the read?
  |     |                               |
  |     |                               +--> YES --> FIX: Read signals before await
  |     |                               +--> NO --> Check equality: signal uses === by default
  |     |                                     --> FIX: Use { equals: false } for objects
  |
  +--> Is the value from a store?
  |     |
  |     +--> Did you destructure the store? const { x } = store
  |     |     --> FIX: Access as store.x in JSX
  |     |
  |     +--> Did you mutate directly? store.x = 5
  |     |     --> FIX: Use setStore("x", 5)
  |     |
  |     +--> Did you use Array.push/splice?
  |     |     --> FIX: Use setStore or produce
  |     |
  |     +--> Did you use setStore correctly?
  |           --> Verify path syntax: setStore("path", "to", "prop", value)
  |
  +--> Is the value from props?
        |
        +--> Did you destructure props? function Comp({ name })
        |     --> FIX: Use function Comp(props) and access props.name
        |
        +--> Did you spread props? 
              --> FIX: Use splitProps/mergeProps for reactive forwarding
```

---

## Tracking Scope Rules

### What IS a Tracking Scope

| Scope | Tracks Dependencies | Re-runs |
|-------|-------------------|---------|
| `createEffect(() => ...)` | YES | When dependencies change |
| `createMemo(() => ...)` | YES | When dependencies change |
| `createRenderEffect(() => ...)` | YES | When dependencies change (sync) |
| `createComputed(() => ...)` | YES | When dependencies change (before render) |
| JSX expressions `{count()}` | YES | When dependencies change |

### What is NOT a Tracking Scope

| Context | Tracks | Why |
|---------|--------|-----|
| Component function body | NO | Runs exactly once during setup |
| Event handlers `onClick={() => ...}` | NO | Run on demand, not reactively |
| `onMount(() => ...)` | NO | Explicitly non-tracking, runs once |
| `setTimeout`/`setInterval` callbacks | NO | Scheduled by the browser, not SolidJS |
| `untrack(() => ...)` | NO | Deliberately suppresses tracking |
| Code after `await` | NO | Async breaks synchronous tracking context |

---

## Async Tracking Loss

The SolidJS reactive system tracks dependencies **synchronously**. Any signal read after an `await` is NOT tracked because the execution resumes in a new microtask outside the original tracking scope.

```typescript
// WRONG — signals after await are NOT tracked
createEffect(async () => {
  const response = await fetch("/api/data");
  console.log(count()); // NOT tracked — effect will NOT re-run when count changes
});

// CORRECT — read all signals BEFORE await
createEffect(() => {
  const currentCount = count(); // Tracked
  const url = apiUrl();         // Tracked

  // Non-tracked async work in a separate scope
  (async () => {
    const response = await fetch(url);
    const data = await response.json();
    setResult(data);
  })();
});
```

---

## SolidJS 2.x Debugging Differences

| Aspect | 1.x Behavior | 2.x Behavior |
|--------|-------------|-------------|
| Update timing | Synchronous propagation | Microtask-batched — reads stale until flush |
| Debugging reads | Values update immediately after set | Values may appear stale until batch flushes |
| Immediate propagation | Default behavior | Use `flush()` when needed |
| Dev warnings | Manual detection only | Built-in warnings for accidental top-level reads |
| Effect model | Single `createEffect` | Split compute/apply — inspect each phase separately |

When debugging 2.x: if a value appears stale immediately after `setSignal()`, this is expected microtask batching behavior. The value updates after the current synchronous execution completes. Use `flush()` only when you explicitly need immediate propagation.

---

## Debugging Strategies

### Strategy 1: Isolation Effect

Create a minimal effect that ONLY reads the suspect signal to verify it fires:

```typescript
// Step 1: Verify the signal itself updates
createEffect(() => {
  console.log("[DEBUG] count =", count());
});

// Step 2: If this fires but your UI does not, the problem is in HOW the signal
// is accessed in your component (destructuring, conditional, async)
```

### Strategy 2: Tracking Scope Verification

Wrap suspect code in `createEffect` to confirm tracking works:

```typescript
// If this logs on changes, tracking works — the bug is elsewhere
createEffect(() => {
  console.log("[DEBUG] store.user.name =", store.user.name);
});
```

### Strategy 3: solid-devtools

Install `solid-devtools` for visual dependency graph inspection:

```bash
npm install --save-dev solid-devtools
```

```typescript
// vite.config.ts
import devtools from "solid-devtools/vite";

export default defineConfig({
  plugins: [devtools(), solidPlugin()],
});
```

```typescript
// Entry point (index.tsx / App.tsx)
import "solid-devtools";
```

The devtools browser extension shows: signal values, dependency graphs, component tree, and which computations re-run on each update.

---

## Reference Links

- [references/methods.md](references/methods.md) -- Debugging tools: solid-devtools setup, console.log strategies, tracking scope identification techniques
- [references/examples.md](references/examples.md) -- Complete debugging scenarios with step-by-step diagnosis and fixes
- [references/anti-patterns.md](references/anti-patterns.md) -- Debugging mistakes that make problems worse

### Official Sources

- https://docs.solidjs.com/concepts/intro-to-reactivity
- https://docs.solidjs.com/concepts/effects
- https://docs.solidjs.com/concepts/stores
- https://github.com/thetarnav/solid-devtools

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [Impertio-Studio](https://github.com/Impertio-Studio)
- **Source:** [Impertio-Studio/SolidJS-Claude-Skill-Package](https://github.com/Impertio-Studio/SolidJS-Claude-Skill-Package)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** yes
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-impertio-studio-solidjs-claude-skill-package-solid-errors-reactivity-debugging
- Seller: https://agentstack.voostack.com/s/impertio-studio
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
