.claude/skills/solidjs/solid-agents/solid-agents-review/SKILL.md
Use when reviewing generated SolidJS code, validating a SolidJS project, or checking for React pattern contamination. Prevents silent reactivity breaks from incorrect signal access, destructured props, wrong control flow, and store mutation errors. Covers signal access patterns, control flow components, props handling, store mutations, event handling, and context usage. Keywords: SolidJS review, code validation, React contamination, signal access, createSignal, Show, For, props, stores.
npx skillsauth add OpenAEC-Foundation/OpenAEC-Workspace-Composer solid-agents-reviewInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Run this checklist on EVERY generated SolidJS code block. Each item has a severity level:
// WRONG -- signal value read once, never updates
const [count, setCount] = createSignal(0);
return <div>{count}</div>;
// CORRECT -- signal called as function, tracked reactively
return <div>{count()}</div>;
// WRONG -- snapshot, never updates
const value = count();
return <div>{value}</div>;
// CORRECT -- call getter where the value is needed
return <div>{count()}</div>;
// WRONG -- extracted once, loses tracking
const currentCount = count();
// CORRECT -- use createMemo for derived state
const doubled = createMemo(() => count() * 2);
// WRONG -- unnecessary effect/signal pair
const [doubled, setDoubled] = createSignal(0);
createEffect(() => setDoubled(count() * 2));
// CORRECT -- createMemo caches derived state
const doubled = createMemo(() => count() * 2);
// WRONG -- name() not tracked when loading() is true
createEffect(() => {
if (loading()) return;
console.log(name());
});
// CORRECT -- access all signals first, then use conditionally
createEffect(() => {
const isLoading = loading();
const currentName = name();
if (isLoading) return;
console.log(currentName);
});
// WRONG -- kills reactivity
function Greeting({ name }: { name: string }) {
return <h1>{name}</h1>;
}
// WRONG -- also kills reactivity
function Greeting(props: { name: string }) {
const { name } = props;
return <h1>{name}</h1>;
}
// CORRECT -- access props directly
function Greeting(props: { name: string }) {
return <h1>{props.name}</h1>;
}
// CORRECT -- preserves reactivity while splitting
const [local, others] = splitProps(props, ["class", "onClick"]);
return <div class={local.class} {...others} />;
// CORRECT -- reactive defaults
const merged = mergeProps({ variant: "primary" }, props);
return <button class={merged.variant}>{props.children}</button>;
// WRONG -- may re-create children on each access
const kids = props.children;
// CORRECT -- resolve and cache children
import { children } from "solid-js";
const resolved = children(() => props.children);
return <div>{resolved()}</div>;
<For> instead of Array.map for list rendering// WRONG -- re-creates all DOM nodes on change
{items().map((item) => <div key={item.id}>{item.name}</div>)}
// CORRECT -- fine-grained updates per item
<For each={items()}>
{(item) => <div>{item.name}</div>}
</For>
<Show> instead of ternary for conditional rendering// WRONG -- can cause unnecessary DOM recreation
{isLoggedIn() ? <Dashboard /> : <Login />}
// CORRECT -- reactive conditional rendering
<Show when={isLoggedIn()} fallback={<Login />}>
<Dashboard />
</Show>
<Switch>/<Match> instead of switch statements or chained ternaries// CORRECT
<Switch fallback={<NotFound />}>
<Match when={route() === "home"}><Home /></Match>
<Match when={route() === "about"}><About /></Match>
</Switch>
key prop -- SolidJS <For> tracks by reference// WRONG -- key prop is ignored in SolidJS
<For each={items()}>
{(item) => <div key={item.id}>{item.name}</div>}
</For>
// CORRECT -- no key needed
<For each={items()}>
{(item) => <div>{item.name}</div>}
</For>
<Index> for arrays of primitives, <For> for arrays of objects// WRONG -- snapshot, loses reactivity
const { username } = store.users[0];
// CORRECT -- access in tracking scope
<span>{store.users[0].username}</span>
// WRONG -- replaces entire state, breaks fine-grained tracking
setStore({ ...store, count: store.count + 1 });
// CORRECT -- surgical path update
setStore("count", (c) => c + 1);
setStore("users", 0, "loggedIn", true);
// CORRECT -- Immer-style mutations applied immutably
setStore(produce((state) => {
state.users.push(newUser);
state.count += 1;
}));
// WRONG -- component body runs once, early return is permanent
function Profile(props: { user: User | null }) {
if (!props.user) return <p>Loading...</p>;
return <div>{props.user.name}</div>;
}
// CORRECT -- use Show for reactive conditional
function Profile(props: { user: User | null }) {
return (
<Show when={props.user} fallback={<p>Loading...</p>}>
{(user) => <div>{user().name}</div>}
</Show>
);
}
// WRONG -- calls handler immediately, assigns return value
<button onClick={handleClick()}>Click</button>
// CORRECT -- passes handler reference
<button onClick={handleClick}>Click</button>
// CORRECT -- arrow function for arguments
<button onClick={() => handleClick(id)}>Click</button>
// CORRECT -- array syntax avoids closure creation
<button onClick={[handleClick, id]}>Click</button>
let ref!: HTMLElement for refs, NOT useRef// CORRECT
let inputRef!: HTMLInputElement;
onMount(() => inputRef.focus());
return <input ref={inputRef} />;
declare module "solid-js" {
namespace JSX {
interface Directives {
clickOutside: () => void;
}
}
}
NEVER allow these in SolidJS code:
| React Pattern | SolidJS Replacement |
|---------------|-------------------|
| useState | createSignal |
| useEffect | createEffect |
| useMemo | createMemo |
| useRef | let ref!: T |
| useCallback | Not needed (no re-renders) |
| useContext | useContext (same name, different import) |
| forwardRef | Pass ref as regular prop |
| React.createElement | SolidJS JSX compiler |
| React.memo | Not needed (no re-renders) |
| useReducer | createStore |
// WRONG -- dependency arrays are a React concept
createEffect(() => {
console.log(count());
}, [count]);
// CORRECT -- automatic tracking, no deps
createEffect(() => {
console.log(count());
});
// WRONG -- React cleanup pattern
createEffect(() => {
const id = setInterval(fn, 1000);
return () => clearInterval(id);
});
// CORRECT -- use onCleanup
createEffect(() => {
const id = setInterval(fn, 1000);
onCleanup(() => clearInterval(id));
});
element={} on Route definitions// WRONG -- React Router pattern
<Route path="/" element={<Home />} />
// CORRECT -- Solid Router pattern
<Route path="/" component={Home} />
Execute these checks in order on every generated SolidJS code block:
react, react-dom, react-router-domuseState, useEffect, useMemo, useRef, useCallback, useReducer, forwardRefsignal() in JSX<For>, <Show>, <Switch> used instead of .map(), ternaries, switch statementsonCleanup() used, NOT return from effects[deps] passed to createEffect/createMemokey={} in JSXlet ref!: T pattern, NOT useRefdevelopment
Use when integrating Vite with a backend framework, rendering Vite assets from server-side templates, or setting up dev/production HTML serving. Prevents incorrect manifest.json traversal and missing CSS chunk resolution in production. Covers build.manifest configuration, .vite/manifest.json structure, ManifestChunk properties, dev mode HTML setup, production rendering, CSS/JS chunk resolution, and modulepreload polyfill. Keywords: backend integration, manifest.json, ManifestChunk, Django, Laravel, Rails, modulepreload.
development
Use when encountering dev server startup failures, HMR issues, proxy errors, CORS blocks, or module not found errors during development. Prevents misconfiguring server.hmr behind reverse proxies and forgetting appType: 'custom' in middleware mode. Covers HMR full-reload debugging, proxy configuration, CORS setup, HTTPS certificates, server.fs.strict violations, port conflicts, WebSocket failures, file watcher issues, and middleware mode. Keywords: dev server, HMR, proxy, CORS, HTTPS, WebSocket, port conflict, server.fs.strict, middleware mode, file watcher.
development
Use when encountering pre-bundling errors, dependency resolution failures, stale cache issues, or slow development server startup. Prevents excluding CJS dependencies from pre-bundling (which breaks runtime module resolution) and misconfiguring optimizeDeps. Covers CJS/ESM conversion failures, missing dependency auto-discovery, optimizeDeps configuration, monorepo linked dependencies, cache invalidation, browser cache staleness, and large dependency tree performance. Keywords: pre-bundling, optimizeDeps, CJS, ESM, cache, dependency resolution, monorepo, node_modules/.vite.
development
Use when encountering Vite build failures, chunk size warnings, or version-specific build errors. Prevents the common mistake of using deprecated rollupOptions in v8 or misconfiguring build targets and minifiers. Covers Rolldown/Rollup bundling failures, CSS minification errors, sourcemap problems, library mode build failures, BundleError handling, and asset processing errors. Keywords: build error, Rolldown, chunk size, sourcemap, library mode, minify, BundleError, rollupOptions, build.target.