solid-js-best-practices

Solid.js best practices for AI-assisted code generation, code review, refactoring, and debugging reactivity issues. Use when writing Solid.js components, auditing SolidJS code, migrating from React to Solid, or fixing signals and fine-grained reactivity bugs. 47 rules across 8 categories (reactivity, components, control flow, state management, refs/DOM, performance, accessibility, testing) ranked by priority.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "solid-js-best-practices" with this command: npx skills add richardcarls/solid-js-best-practices/richardcarls-solid-js-best-practices-solid-js-best-practices

Solid.js Best Practices

Comprehensive best practices for building Solid.js applications and components, optimized for AI-assisted code generation, review, and refactoring.

Quick Reference

Essential Imports

import {
  createSignal,
  createEffect,
  createMemo,
  createResource,
  onMount,
  onCleanup,
  Show,
  For,
  Switch,
  Match,
  Index,
  Suspense,
  ErrorBoundary,
  lazy,
  batch,
  untrack,
  mergeProps,
  splitProps,
  children,
} from "solid-js";

import { createStore, produce, reconcile } from "solid-js/store";

Component Skeleton

import { Component, JSX, mergeProps, splitProps } from "solid-js";

interface MyComponentProps {
  title: string;
  count?: number;
  onAction?: () => void;
  children?: JSX.Element;
}

const MyComponent: Component<MyComponentProps> = (props) => {
  // Merge default props
  const merged = mergeProps({ count: 0 }, props);

  // Split component props from passed-through props
  const [local, others] = splitProps(merged, ["title", "count", "onAction"]);

  // Local reactive state
  const [value, setValue] = createSignal("");

  // Derived/computed values
  const doubled = createMemo(() => local.count * 2);

  // Side effects
  createEffect(() => {
    console.log("Count changed:", local.count);
  });

  // Lifecycle
  onMount(() => {
    console.log("Component mounted");
  });

  onCleanup(() => {
    console.log("Component cleanup");
  });

  return (
    <div {...others}>
      <h1>{local.title}</h1>
      <p>Count: {local.count}, Doubled: {doubled()}</p>
      <input
        value={value()}
        onInput={(e) => setValue(e.currentTarget.value)}
      />
      <button onClick={local.onAction}>Action</button>
      {props.children}
    </div>
  );
};

export default MyComponent;

Rules by Category

1. Reactivity (7 rules)

#RulePriorityDescription
1-1Use Signals CorrectlyCRITICALAlways call signals as functions count() not count
1-2Use Memo for Derived ValuesHIGHUse createMemo for computed values, not createEffect
1-3Effects for Side Effects OnlyHIGHUse createEffect only for side effects, not derivations
1-7No Primitives in Reactive ContextsHIGHDon't call hooks or create reactive primitives inside effects or memos
1-4Avoid Setting Signals in EffectsMEDIUMSetting signals in effects can cause infinite loops
1-5Use Untrack When NeededMEDIUMUse untrack() to prevent unwanted reactive subscriptions
1-6Batch Signal UpdatesLOWUse batch() for multiple synchronous signal updates

2. Components (9 rules)

#RulePriorityDescription
2-1Never Destructure PropsCRITICALDestructuring props breaks reactivity
2-6Components Return OnceCRITICALNever use early returns — use <Show>, <Switch>, etc. in JSX
2-9Never Call Components as FunctionsCRITICALAlways use JSX or createComponent() — direct calls leak reactive scope
2-2Use mergePropsHIGHUse mergeProps for default prop values
2-3Use splitPropsHIGHUse splitProps to separate prop groups safely
2-7No React-Specific PropsHIGHUse class not className, for not htmlFor
2-4Use children HelperMEDIUMUse children() helper for safe children access
2-5Prefer CompositionMEDIUMPrefer composition and context over prop drilling
2-8Style Prop ConventionsMEDIUMUse object syntax with kebab-case properties for style

3. Control Flow (6 rules)

#RulePriorityDescription
3-1Use Show for ConditionalsHIGHUse <Show> instead of ternary operators
3-2Use For for ListsHIGHUse <For> for referentially-keyed list rendering
3-3Use Index for PrimitivesMEDIUMUse <Index> when array index matters more than identity
3-4Use Switch/MatchMEDIUMUse <Switch>/<Match> for multiple conditions
3-6Stable Component MountMEDIUMAvoid rendering the same component in multiple Switch/Show branches
3-5Provide FallbacksLOWAlways provide fallback props for loading states

4. State Management (5 rules)

#RulePriorityDescription
4-1Signals vs StoresHIGHUse signals for primitives, stores for nested objects
4-2Use Store Path SyntaxHIGHUse path syntax for granular, efficient store updates
4-3Use produce for MutationsMEDIUMUse produce for complex mutable-style store updates
4-4Use reconcile for Server DataMEDIUMUse reconcile when integrating server/external data
4-5Use Context for Global StateMEDIUMUse Context API for cross-component shared state

5. Refs & DOM (6 rules)

#RulePriorityDescription
5-1Use Refs CorrectlyHIGHUse callback refs for conditional elements
5-2Access DOM in onMountHIGHAccess DOM elements in onMount, not during render
5-3Cleanup with onCleanupHIGHAlways clean up subscriptions and timers
5-5Avoid innerHTMLHIGHAvoid innerHTML to prevent XSS — use JSX or textContent
5-4Use DirectivesMEDIUMUse use: directives for reusable element behaviors
5-6Event Handler PatternsMEDIUMUse on:/oncapture: namespaces and array handler syntax correctly

6. Performance (5 rules)

#RulePriorityDescription
6-1Avoid Unnecessary TrackingHIGHDon't access signals outside reactive contexts
6-2Use Lazy ComponentsMEDIUMUse lazy() for code splitting large components
6-3Use SuspenseMEDIUMUse <Suspense> for async loading boundaries
6-4Optimize Store AccessLOWAccess only the store properties you need
6-5Prefer classListLOWUse classList prop for conditional class toggling

7. Accessibility (3 rules)

#RulePriorityDescription
7-1Use Semantic HTMLHIGHUse appropriate semantic HTML elements
7-2Use ARIA AttributesMEDIUMApply appropriate ARIA attributes for custom controls
7-3Support Keyboard NavigationMEDIUMEnsure all interactive elements are keyboard accessible

8. Testing (6 rules)

#RulePriorityDescription
8-1Configure Vitest for SolidCRITICALConfigure Vitest with Solid-specific resolve conditions and plugin
8-2Wrap Render in Arrow FunctionsCRITICALAlways use render(() => <C />) not render(<C />)
8-3Test Primitives in a RootHIGHWrap signal/effect/memo tests in createRoot or renderHook
8-4Handle Async in TestsHIGHUse findBy queries and proper timer config for async behavior
8-5Use Accessible QueriesMEDIUMPrefer role and label queries over test IDs
8-6Separate Logic from UI TestsMEDIUMTest primitives/hooks independently from component rendering

Task-Based Rule Selection

Writing New Components

Load these rules when creating new Solid.js components:

RuleWhy
1-1Ensure signals are called as functions
2-1Prevent reactivity breakage
2-6No early returns — use control flow in JSX
2-9Never call components as plain functions
2-2Handle default props correctly
2-3Separate local and forwarded props
3-1Proper conditional rendering
3-2Efficient list rendering
5-3Prevent memory leaks

Code Review

Focus on these rules during code review:

PriorityRules
CRITICAL1-1, 2-1, 2-6, 2-9
HIGH1-2, 1-3, 1-7, 2-7, 5-2, 5-3, 5-5

Performance Optimization

Load these rules when optimizing performance:

RuleFocus
1-2Prevent unnecessary recomputation
1-6Reduce update cycles
4-2Granular store updates
6-1Prevent unwanted subscriptions
6-2Code splitting
6-4Efficient store access

State Management

Load these rules when working with application state:

RuleFocus
4-1Choose the right primitive
4-2Efficient updates
4-3Complex mutations
4-4External data integration
4-5Cross-component state

Accessibility Audit

Load these rules when auditing accessibility:

RuleFocus
7-1Semantic structure
7-2Screen reader support
7-3Keyboard users

Writing Tests

Load these rules when writing or reviewing tests:

RuleFocus
8-1Correct Vitest configuration
8-2Reactive render scope
8-3Reactive ownership for primitives
8-4Async queries and timers
8-5Accessible query selection
8-6Test architecture

Common Mistakes to Catch

MistakeRuleSolution
Forgetting () on signal access1-1Always call signals: count()
Destructuring props2-1Access via props.name
Using ternaries for conditionals3-1Use <Show> component
.map() for lists3-2Use <For> component
Deriving values in effects1-2Use createMemo
Setting signals in effects1-4Use createMemo or external triggers
Accessing DOM during render5-2Use onMount
Forgetting cleanup5-3Use onCleanup
Early returns in components2-6Use <Show>, <Switch> in JSX instead
Using className or htmlFor2-7Use class and for (standard HTML)
style="color: red" or camelCase styles2-8Use style={{ color: "red" }} with kebab-case
Using innerHTML with user data5-5Use JSX or sanitize with DOMPurify
Spreading whole store6-4Access specific properties
String concatenation for class toggling6-5Use classList={{ active: isActive() }}
render(<Comp />) without arrow8-2Use render(() => <Comp />)
Effects in tests without owner8-3Wrap in createRoot or use renderHook
getBy for async content8-4Use findBy queries
MyComp(props) instead of <MyComp />2-9Always use JSX syntax or createComponent()
Calling useMatch()/useQuery() inside createEffect/createComputed1-7Call hooks once at component init, not inside reactive computations
Same component in Switch fallback and Match branch3-6Keep component in one stable position; use CSS for layout changes

Solid.js vs React Mental Model

When helping users familiar with React, keep these differences in mind:

ReactSolid.js
Components re-render on state changeComponents run once, signals update DOM directly
useState returns [value, setter]createSignal returns [getter, setter]
useMemo with deps arraycreateMemo with automatic tracking
useEffect(fn, [deps])createEffect(fn) (no deps array — automatic tracking)
Destructure props freelyNever destructure props
Early returns (if (!x) return null)<Show> / <Switch> in JSX (components return once)
{condition && <Component />}<Show when={condition}><Component /></Show>
{items.map(item => ...)}<For each={items}>{item => ...}</For>
classNameclass
htmlForfor
style={{ fontSize: 14 }}style={{ "font-size": "14px" }}
Context requires useContext hookContext works with useContext or direct access

Priority Levels

  • CRITICAL: Fix immediately. Causes bugs, broken reactivity, or runtime errors.
  • HIGH: Address in code reviews. Important for correctness and maintainability.
  • MEDIUM: Apply when relevant. Improves code quality and performance.
  • LOW: Consider during refactoring. Nice-to-have optimizations.

Key Solid.js Concepts

Fine-Grained Reactivity

Solid.js updates only the specific DOM elements that depend on changed data, not entire component trees. This is achieved through:

  • Signals: Reactive primitives that track dependencies
  • Effects: Side effects that automatically re-run when dependencies change
  • Memos: Cached derived values that only recompute when dependencies change

Components Render Once

Unlike React, Solid components are functions that run once during initial render. Reactivity happens at the signal level, not the component level. This is why:

  • Props must not be destructured (would capture static values)
  • Signals must be called as functions (to maintain reactive tracking)
  • Control flow uses special components (<Show>, <For>) instead of JS expressions

Stores for Complex State

For nested objects and arrays, Solid provides stores with:

  • Fine-grained updates via path syntax
  • Automatic proxy wrapping for nested reactivity
  • Utilities like produce and reconcile for common patterns

Tooling

For automated linting alongside these best practices, use eslint-plugin-solid. The plugin catches many of the same issues this skill covers (destructured props, early returns, React-specific props, innerHTML usage, style prop format, etc.) and provides auto-fixable rules.

Resources

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Security

Ring Security

Monitor and manage Ring doorbells and security cameras. Query device status, review motion events, manage modes, and export event history. Use when you need...

Registry SourceRecently Updated
Security

Retirement Planner

退休规划工具。退休金计算、投资策略、社保养老金、投资组合、提取策略、缺口分析。Retirement planner with pension calculation, investment strategy, social security, portfolio, withdrawal strategy, gap...

Registry SourceRecently Updated
Security

Node Auto Debugger

Scan Node.js/Express/Next.js projects for bugs, security issues, and anti-patterns. Use when debugging a Node.js web app, running code audits, fixing client-...

Registry SourceRecently Updated
Security

Shieldcortex

Persistent memory system with security for AI agents. Remembers decisions, preferences, architecture, and context across sessions with knowledge graphs, deca...

Registry SourceRecently Updated
6540Profile unavailable