solid-js-1.x-best-practices-and-api — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited solid-js-1.x-best-practices-and-api (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Use this skill for SolidJS 1.x core framework work. Keep it focused on idiomatic Solid usage, not SolidStart-specific routing or server patterns.
Start by identifying whether the user needs one of these modes:
When reviewing or editing code, prefer minimal changes that preserve behavior.
Default to explaining the smallest Solid-specific reason a pattern is wrong. Avoid turning a local fix into a large rewrite unless the user asked for that.
Read this skill in passes instead of treating every section as equally important.
Core mental model and Primitive selection guide.Core best practices when writing or refactoring ordinary component code.Specialized rule packs and Task-focused rule selection when the task is about testing, accessibility, web components, or performance work.Common mistakes to catch and Debugging checklist for code review or bug triage.createMemo, createEffect, and similar reactive scopes.Use that model to explain bugs and justify fixes.
Choose primitives with this bias:
createSignal for local scalar statecreateMemo for computed valuescreateStore for nested structured statecreateResource plus Suspense for async data loadingcreateEffect only when touching the outside worldShow, For, and Index for UI control flowsplitProps and mergeProps when reshaping propsKeep these ideas in the foreground:
Use the smallest primitive that matches the job.
createSignalcount() and write with setCount(next) or setCount(prev => next).createMemomemo().createEffectonCleanup.createResourceSuspense for loading UI.createEffect(async () => ...) so loading and reactivity stay aligned with Solid's async model.batchcreateStorestore.user.name, and prefer targeted updates.produce, reconcile, and unwrapproduce for ergonomic nested mutations.reconcile when syncing store state to incoming server or external object graphs.unwrap when a plain non-reactive value is required.splitProps and mergePropssplitProps when you need local and rest prop groupings without breaking reactivity.mergeProps for layered defaults and overrides.Showwhen={condition} and optional fallback={...}.&& rendering and large ternaries.Foreach={items()}..map(...) in dynamic UI code because it preserves identity and updates lists efficiently.IndexIndex; choose it only when position-based retention is the real goal.Suspense and ErrorBoundarySuspense with async reads such as createResource.ErrorBoundary where async or rendering failures should be isolated.loading, error, and ready booleans scattered through the tree.lazySuspense.onCleanupuntrackuntrack is the wrong tool.createContext and useContextuseContext.props.name or preserve them with splitProps.Prefer:
function App() {
const [id] = createSignal(0);
return <User id={id()} name="Brenley" />;
}
function User(props: { id: number; name: string }) {
return <h1>{props.id} - {props.name}</h1>;
}createMemo.createMemo when the computation is reused, non-trivial, or clearly benefits from caching.createEffect unless the job is actually synchronizing with the outside world.Common anti-pattern:
const [firstName, setFirstName] = createSignal("John");
const [lastName, setLastName] = createSignal("Doe");
const [fullName, setFullName] = createSignal("");
createEffect(() => {
setFullName(`${firstName()} ${lastName()}`);
});Prefer:
const fullName = createMemo(() => `${firstName()} ${lastName()}`);For async reads, prefer createResource over createEffect(async () => ...).
Show, For, Index, Switch, and Match over ad-hoc &&, ternaries, and .map(...) in JSX.createStore when nested object shape and property-level updates matter.splitProps and mergeProps when reshaping props.createEffect for DOM APIs, browser APIs, logging, subscriptions, and third-party integrations.onCleanup.If the job is ordinary rendering, keep it declarative. Use the rule packs below for testing, accessibility, web-component, and performance-specific guidance.
When a Solid 1.x component is not updating correctly, check these first:
createEffect being used to mirror state that should be derived?splitProps or mergeProps be used instead of destructuring?createStore better fit the shape of the state?untrack or batch used intentionally, or is it masking the real problem?Use these rule packs when the task is more specialized than the general reactivity guidance above.
Treat the rule IDs as lookup labels inside this skill. They are there to make review checklists and task-focused loading more compact.
| Rule | Priority | Description |
|---|---|---|
| 3-1 Use Show for Conditionals | HIGH | Use <Show> instead of ternary operators for primary conditional UI branches. |
| 3-2 Use For for Lists | HIGH | Use <For> for referentially-keyed list rendering. |
| 3-3 Use Index for Primitives | MEDIUM | Use <Index> when array position matters more than item identity. |
| 3-4 Use Switch/Match | MEDIUM | Use <Switch> and <Match> for multiple conditions instead of deeply nested ternaries. |
| 3-5 Provide Fallbacks | LOW | Prefer explicit fallback props for loading or empty states. |
| 3-6 Stable Component Mount | MEDIUM | Avoid rendering the same component in multiple Show or Switch branches; keep it in one stable position when possible. |
| Rule | Priority | Description |
|---|---|---|
| 4-1 Signals vs Stores | HIGH | Use signals for primitives and simple references, stores for nested objects and arrays. |
| 4-2 Use Store Path Syntax | HIGH | Use store path syntax for granular, efficient updates. |
| 4-3 Use produce for Mutations | MEDIUM | Use produce for complex mutable-style store updates. |
| 4-4 Use reconcile for Server Data | MEDIUM | Use reconcile when integrating server or external data into an existing store shape. |
| 4-5 Use Context for Global State | MEDIUM | Use context for shared cross-component state or capabilities. |
| Rule | Priority | Description |
|---|---|---|
| 5-1 Use Refs Correctly | HIGH | Use callback refs for conditional elements and imperative handles that may appear later. |
| 5-2 Access DOM in onMount | HIGH | Access DOM elements in onMount, not during render. |
| 5-3 Cleanup with onCleanup | HIGH | Always clean up subscriptions, timers, listeners, and imperative integrations. |
| 5-4 Use Directives | MEDIUM | Use use: directives for reusable element behaviors in Solid 1.x. |
| 5-5 Avoid innerHTML | HIGH | Avoid innerHTML with unsanitized content; prefer JSX or textContent. |
| 5-6 Event Handler Patterns | MEDIUM | Use on: and oncapture: namespaces and array handler syntax correctly when the platform or library expects them. |
| 5-7 Web Component Controlled State | HIGH | Use createEffect, refs, and imperative calls to sync signals to web component APIs when declarative attributes are not enough. |
| Rule | Priority | Description |
|---|---|---|
| 6-1 Avoid Unnecessary Tracking | HIGH | Do not access signals outside reactive contexts unless the read is intentionally static. |
| 6-2 Use Lazy Components | MEDIUM | Use lazy() for code splitting when large components do not need to be in the initial bundle. |
| 6-3 Use Suspense | MEDIUM | Use <Suspense> for async loading boundaries instead of ad-hoc loading booleans everywhere. |
| 6-4 Optimize Store Access | LOW | Read only the store properties you actually need. |
| 6-5 Prefer classList | LOW | Use classList for conditional class toggling in Solid 1.x. |
| 6-6 Web Component CSS and Bundle Strategy | MEDIUM | Import custom elements individually when possible, and keep ::part() overrides in a global stylesheet rather than CSS modules. |
| Rule | Priority | Description |
|---|---|---|
| 7-1 Use Semantic HTML | HIGH | Use the right semantic element before reaching for ARIA. |
| 7-2 Use ARIA Attributes | MEDIUM | Add appropriate ARIA attributes for custom controls and landmark exposure. |
| 7-3 Support Keyboard Navigation | MEDIUM | Make sure interactive elements are reachable and usable from the keyboard. |
| Rule | Priority | Description |
|---|---|---|
| 8-1 Configure Vitest for Solid | CRITICAL | Configure Vitest with the Solid plugin and Solid-specific resolve conditions. |
| 8-2 Wrap Render in Arrow Functions | CRITICAL | Always use render(() => <Comp />), not render(<Comp />). |
| 8-3 Test Primitives in a Root | HIGH | Wrap signal, memo, and effect tests in createRoot or use renderHook. |
| 8-4 Handle Async in Tests | HIGH | Use findBy queries and correct timer configuration for async behavior. |
| 8-5 Use Accessible Queries | MEDIUM | Prefer role and label queries over test IDs. |
| 8-6 Separate Logic from UI Tests | MEDIUM | Test reactive primitives independently from component rendering when possible. |
| 8-7 Browser Mode for Web Components and PWA APIs | HIGH | Use Vitest browser mode for custom elements, shadow DOM, and browser-native APIs. |
| 8-8 Testing Headless UI Libraries with Non-Standard ARIA | MEDIUM | Inspect the real tree and portal structure before choosing queries. |
| 8-9 Browser-Native API Test Isolation | HIGH | Clear IndexedDB and localStorage between tests, and close connections before deleteDatabase. |
| 8-10 Router Integration Testing | HIGH | Use MemoryRouter root setup to provide router context to layout and provider trees. |
| 8-11 TanStack Query Test Setup | HIGH | Create a fresh QueryClient per test with retries and caching disabled. |
Load these rules first when creating new Solid 1.x components:
ShowForonCleanupBias code review toward these priorities:
Load these rules when optimizing performance:
Load these rules when the task is mostly about application state:
produce for complex mutationsreconcile for external data integrationLoad these rules when auditing accessibility:
Load these rules when writing or reviewing tests:
Load these rules when the user is using Shoelace, FAST, Lion, Material Web Components, native <dialog>, the Popover API, or similar browser-native imperative APIs:
on: for custom element events and type CustomEvent payloads correctly::part() overrides in global CSS| Mistake | Rule | Solution |
|---|---|---|
Forgetting () on signal access | 1-1 | Always call signals like count(). |
| Destructuring props | 2-1 | Access them via props.name or splitProps. |
| Using ternaries for primary conditionals | 3-1 | Prefer <Show>. |
Using .map() for dynamic lists | 3-2 | Prefer <For>. |
| Deriving values in effects | 1-2 | Prefer createMemo or a derived function. |
| Setting signals in effects to mirror other state | 1-4 | Derive the value or trigger updates from the real source. |
| Accessing DOM during render | 5-2 | Use onMount. |
| Forgetting cleanup | 5-3 | Use onCleanup. |
| Early returns in components | 2-6 | Use <Show> or <Switch> in JSX. |
Using className or htmlFor | 2-7 | Use class and for. |
Using style="color: red" or camelCase inline styles | 2-8 | Use style={{ color: "red" }} and keep CSS property names platform-correct. |
Using innerHTML with user data | 5-5 | Use JSX or sanitize with a library such as DOMPurify first. |
| Spreading a whole store when only one field is needed | 6-4 | Read specific properties. |
| String concatenation for conditional classes | 6-5 | Prefer classList={{ active: isActive() }}. |
render(<Comp />) without an arrow | 8-2 | Use render(() => <Comp />). |
| Effects or memos in tests without an owner | 8-3 | Wrap them in createRoot or use renderHook. |
getBy for async content | 8-4 | Use findBy. |
Calling MyComp(props) instead of <MyComp /> | 2-9 | Use JSX or createComponent(). |
Calling router hooks like useMatch() inside createEffect | 1-7 | Call hooks once at component init, not inside reactive computations. |
Rendering the same component in multiple Switch branches | 3-6 | Keep it mounted in one stable position. |
| Custom elements not upgrading in tests | 8-7 | Use browser mode instead of jsdom. |
| IndexedDB state leaking between tests | 8-9 | Close the connection before deleteDatabase and reset state between tests. |
| Router primitives throwing missing-route errors | 8-10 | Provide router context with MemoryRouter. |
| Query retries masking failures in tests | 8-11 | Use a fresh QueryClient with retries disabled. |
waitFor(length === 0) passing before data loads | 8-4 | Use a settled anchor like findBy before asserting absence. |
getByRole('form') failing even though the form exists | 7-2 | Add aria-label or aria-labelledby so the form has an accessible name. |
<my-element onMyChange={...}> missing custom events | 5-6 | Use on:my-change. |
::part() rules inside CSS modules not applying | 6-6 | Move them to a global stylesheet. |
| Barrel imports for whole web component libraries | 6-6 | Import only the components you use. |
value={signal()} on a custom element not syncing state | 5-7 | Listen for events and push values imperatively through the element ref. |
<div popover> or <button popoverTarget="x"> TypeScript error | 2-10 | Augment the relevant HTMLElement types in a .d.ts file for newer HTML attributes. |
Object props on custom elements becoming "[object Object]" | 5-7 | Use prop:myProp={value} when the library expects a JS property. |
Experimental CSS properties such as anchor-name causing a TypeScript error | 2-8 | Cast through unknown to JSX.CSSProperties instead of forcing never. |
When the user is thinking in React terms, keep these comparisons handy:
| React | Solid.js 1.x |
|---|---|
| Components re-render on state change | Components run once; signals update DOM directly |
useState() returns [value, setter] | createSignal() returns [getter, setter] |
useMemo(fn, deps) | createMemo(fn) with automatic tracking |
useEffect(fn, deps) | createEffect(fn) with automatic tracking |
| Destructure props freely | Preserve prop getters; do not destructure by default |
Early returns like if (!x) return null | Prefer <Show> or <Switch> in JSX |
{condition && <Comp />} | <Show when={condition}><Comp /></Show> |
{items.map(item => ...)} | <For each={items}>{item => ...}</For> |
className | class |
htmlFor | for |
style={{ fontSize: 14 }} | style={{ "font-size": "14px" }} when needed for CSS-value correctness |
React 19 custom element events can look like onMyEvent | Solid uses on:my-event for custom element events |
For automated linting alongside these rules, prefer eslint-plugin-solid. It catches many of the same issues around destructured props, early returns, React-style props, style prop format, and unsafe patterns such as innerHTML misuse.
When helping the user, prefer this structure:
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.