$ npx rulesync-cli pull✓ Wrote CLAUDE.md (2 rulesets)# Coding Standards- Always use async/await- Prefer named exports
Rule Writing

CLAUDE.md for SolidJS Projects

SolidJS looks like React but works completely differently. AI generates React patterns that break Solid's fine-grained reactivity. Rules for signals, effects, and no-VDOM patterns.

7 min read·August 11, 2025

SolidJS looks like React but every React assumption breaks it

No destructuring props, no .map(), no ternary — fine-grained reactivity rules

Why SolidJS Needs Rules That Actively Prevent React Patterns

SolidJS is the framework that looks most like React but works most differently. JSX syntax is identical. The API names are similar (createSignal ≈ useState, createEffect ≈ useEffect). But the execution model is fundamentally different: Solid components run once (not on every re-render), reactivity is fine-grained (not component-level), and destructuring props breaks reactivity. AI assistants that see JSX assume React — and every React assumption breaks Solid.

The most dangerous AI failures: destructuring props (kills reactivity tracking), using array index as key in loops (Solid doesn't need keys — it tracks by reference), treating components as functions that re-run (they don't — only the reactive expressions update), and using React hooks (useState, useEffect, useMemo — none exist in Solid).

SolidJS rules must actively say 'this is not React' — because the AI will assume it is. Every rule prevents a specific React pattern that breaks Solid's reactivity system.

Rule 1: Signals and Fine-Grained Reactivity

The rule: 'Use createSignal for reactive state: const [count, setCount] = createSignal(0). Access signal values by calling them as functions: count() — not count. Signals update only the DOM nodes that depend on them — not the entire component. Never destructure signals from objects: const { name } = user breaks tracking. Access as user.name or user().name depending on the source.'

For derived values: 'Use functions for derived values — no createMemo unless the computation is expensive. const doubled = () => count() * 2 is reactive and zero-cost. Use createMemo only when the derived computation is expensive and should be cached: const sorted = createMemo(() => expensiveSort(items())).'

For effects: 'Use createEffect for side effects that depend on signals: createEffect(() => { document.title = `Count: ${count()}` }). Effects track their dependencies automatically — no dependency array needed (unlike React's useEffect). Use onCleanup inside effects for teardown. Effects run synchronously after signal updates.'

  • createSignal: const [val, setVal] = createSignal(initial) — call val() to read
  • Functions for derived: const doubled = () => count() * 2 — zero-cost, reactive
  • createMemo only for expensive computations — functions for simple derivations
  • createEffect for side effects — auto-tracks dependencies, no dependency array
  • onCleanup inside effects for teardown — onMount for one-time setup
ℹ️ Components Run Once

Solid components are NOT functions that re-run on state change (like React). They run once to set up reactive bindings. Only the specific DOM expressions that depend on a signal update. This is why destructuring breaks — it captures values at setup time.

Rule 2: Never Destructure Props

The rule: 'Never destructure props in SolidJS. Destructuring breaks reactive tracking: function User({ name }) reads name once at component creation and never updates. Use props.name to maintain reactivity: function User(props) { return <div>{props.name}</div> }. If you need local aliases, use the mergeProps or splitProps utilities.'

For splitting props: 'Use splitProps to separate props for forwarding: const [local, rest] = splitProps(props, ["class", "style"]). Use mergeProps for default values: const merged = mergeProps({ color: "blue" }, props). Never use spread: {...props} without splitProps — it breaks reactivity on the spread properties.'

This is the most critical SolidJS rule — and the one AI violates most often. In React, destructuring props is standard practice. In Solid, it's a bug. The AI sees JSX and immediately destructures. One rule prevents the most common Solid reactivity bug.

⚠️ Most Critical Rule

Destructuring props kills reactivity in SolidJS. function User({ name }) reads name once and never updates. function User(props) { return props.name } stays reactive. AI destructures by instinct — this rule is life-or-death.

Rule 3: Control Flow with Show, For, Switch

The rule: 'Use Solid's control flow components for conditional and list rendering. <Show when={condition()} fallback={<Loading />}> for conditionals — never ternary in JSX (it re-creates DOM nodes). <For each={items()}> for lists — never .map() (it re-creates every item on update). <Switch>/<Match> for multi-branch conditionals. <Dynamic component={comp}> for dynamic components.'

For why this matters: 'React re-renders the entire component tree on state change — ternary and .map() work because everything rebuilds anyway. Solid only updates the specific DOM nodes that changed. Ternary creates and destroys DOM nodes; <Show> toggles visibility. .map() recreates all elements; <For> updates only changed elements. The performance difference is dramatic.'

For keyed vs unkeyed: '<For each={items()}>{(item) => <div>{item.name}</div>}</For> tracks by reference — no key prop needed (unlike React). Use <Index> when you want to track by array index instead of reference (rare — usually for fixed-position arrays like grid cells).'

  • <Show when={}> for conditionals — never ternary {x ? <A/> : <B/>}
  • <For each={}> for lists — never .map() (re-creates all elements)
  • <Switch>/<Match> for multi-branch — <Dynamic> for dynamic components
  • No key prop needed — <For> tracks by reference automatically
  • <Index> for index-tracked iteration (rare — grid cells, fixed arrays)
💡 <Show> Not Ternary

React ternary (x ? <A/> : <B/>) creates and destroys DOM nodes on every toggle. Solid's <Show> toggles visibility without DOM recreation. Same for <For> vs .map() — <For> updates changed elements, .map() recreates all of them.

Rule 4: Stores and Context

The rule: 'Use createStore for complex reactive state (objects, arrays, nested data): const [state, setState] = createStore({ users: [], loading: false }). Stores are deeply reactive — accessing state.users[0].name tracks that specific path. Use setState with path syntax: setState("users", 0, "name", "Alice"). Use createContext and useContext for dependency injection — same pattern as React but with reactive values.'

For store updates: 'Use path-based updates for surgical modifications: setState("users", i => i.active, "role", "admin") updates the role of all active users. Use produce (from solid-js/store) for Immer-like mutable updates: setState(produce(s => { s.users.push(newUser) })). Never replace the entire store — update specific paths for fine-grained reactivity.'

For global state: 'Create a context provider with a store: const UserContext = createContext<ReturnType<typeof createUserStore>>(); Wrap the app with <UserProvider>. Access in children with useContext(UserContext). This is Solid's equivalent of Zustand/Redux — but built into the framework with fine-grained reactivity.'

Rule 5: SolidStart Conventions

The rule: 'For SolidStart projects: use server functions ("use server") for data fetching: createAsync(fetchUser) in the component, server function defined with "use server" directive. Use actions for mutations. Use file-based routing in routes/ directory. Use routeData for loader-like data fetching. Use <A> component for navigation.'

For server functions: 'Server functions are the bridge between client and server — similar to Next.js server actions but for both reads and writes. Define with "use server" at function level. They run on the server and are called from the client via RPC. Use cache() for cached data fetching.'

For error handling: 'Use <ErrorBoundary> for component-level error isolation. Use createResource with error handling for async data. SolidStart provides HttpStatusCode component for setting status codes in SSR: <HttpStatusCode code={404} /> in error pages.'

Complete SolidJS Rules Template

Consolidated rules for SolidJS projects.

  • This is NOT React: components run once, reactivity is fine-grained, no virtual DOM
  • createSignal — call val() to read — never destructure signal values
  • Never destructure props — use props.x, splitProps, mergeProps
  • <Show> for conditionals, <For> for lists — never ternary or .map()
  • createStore for complex state — path-based setState for surgical updates
  • Functions for derived values — createMemo only for expensive computations
  • createEffect with auto-tracking — no dependency arrays
  • SolidStart: server functions, actions, file routing, createAsync