agentsclimarketplace

Solid js 1.x best practices and api

Skill doeixd/solid-skill/solid-js-1.x-best-practices-and-api

Agent skills for SolidJS 1.x, Solid 2.x migration and API guidance, and SolidStart v2 workflows.

Install
npx -y skills add doeixd/solid-skill --skill solid-js-1.x-best-practices-and-api

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
  • 7 stars7 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Guidance for writing, reviewing, and refactoring SolidJS 1.x code using idiomatic APIs and reactivity patterns. Use this whenever the user is working in SolidJS 1.x and asks for help with components, signals, props, memos, effects, stores, control flow, or why a Solid component is not updating correctly. Also use it for code reviews or refactors of Solid 1.x code, especially when React habits may be causing non-idiomatic Solid patterns.

SKILL.md

23.1 KB, ~5.5k tokens by cl100k_base, as published. Nobody here has run it

SolidJS 1.x Best Practices And API

Use this skill for SolidJS 1.x core framework work. Keep it focused on idiomatic Solid usage, not SolidStart-specific routing or server patterns.

Goals

  • Preserve Solid 1.x reactivity instead of accidentally breaking it with destructuring or early reads.
  • Prefer declarative derivation over effect-driven synchronization.
  • Use the Solid control-flow and state primitives that match the problem.
  • Explain recommendations in terms of Solid's fine-grained reactivity model.

Working style

Start by identifying whether the user needs one of these modes:

  • explanation of a Solid 1.x concept
  • debugging of stale or non-updating UI
  • refactor toward more idiomatic Solid 1.x code
  • code review of a Solid component or state pattern

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.

How to use this skill

Read this skill in passes instead of treating every section as equally important.

  1. Start with Core mental model and Primitive selection guide.
  2. Use Core best practices when writing or refactoring ordinary component code.
  3. Use Specialized rule packs and Task-focused rule selection when the task is about testing, accessibility, web components, or performance work.
  4. Use Common mistakes to catch and Debugging checklist for code review or bug triage.

Core mental model

  • Components run once for setup.
  • Reactive tracking happens when signals or reactive props are read inside JSX, createMemo, createEffect, and similar reactive scopes.
  • If a read happens too early, outside tracking, it will not stay live.
  • Props are reactive by default in Solid 1.x, so preserve their getters.

Use that model to explain bugs and justify fixes.

Primitive selection guide

Choose primitives with this bias:

  1. createSignal for local scalar state
  2. derived function or createMemo for computed values
  3. createStore for nested structured state
  4. createResource plus Suspense for async data loading
  5. createEffect only when touching the outside world
  6. Show, For, and Index for UI control flow
  7. splitProps and mergeProps when reshaping props
  8. context only for genuinely shared subtree concerns

Core heuristics

Keep these ideas in the foreground:

  1. Keep getters intact.
  2. Derive instead of synchronizing.
  3. Let JSX and reactive scopes own dependency tracking.
  4. Use effects for the outside world, not for routine data flow.
  5. Use stores when the shape of nested data matters.

Core primitives

Use the smallest primitive that matches the job.

createSignal

  • Use it for local scalar state and simple references.
  • Read with count() and write with setCount(next) or setCount(prev => next).
  • Prefer stores instead when nested object shape and fine-grained branch updates matter.

createMemo

  • Use it for cached derived state that is reused or non-trivial.
  • Read with memo().
  • Do not use it for side effects or state writes.

createEffect

  • Use it as the side-effect boundary for DOM APIs, browser APIs, logging, and third-party libraries.
  • Clean up long-lived work with onCleanup.
  • Do not use it as the default way to derive or synchronize state; check whether the real need is a memo, resource, store helper, or plain JSX.

createResource

  • Use it for fetch-like UI data loading.
  • Pair it with Suspense for loading UI.
  • Prefer it over createEffect(async () => ...) so loading and reactivity stay aligned with Solid's async model.

batch

  • Use it to group synchronous setter calls when downstream computations should observe the combined update.
  • Treat it as a targeted optimization or coordination tool, not a vague performance ritual.

createStore

  • Use it for nested objects and arrays when property-level reactivity matters.
  • Read properties directly, like store.user.name, and prefer targeted updates.
  • If updates are mostly whole-object replacement and nested granularity does not matter, a signal may be simpler.

produce, reconcile, and unwrap

  • Use produce for ergonomic nested mutations.
  • Use reconcile when syncing store state to incoming server or external object graphs.
  • Use unwrap when a plain non-reactive value is required.

splitProps and mergeProps

  • Use splitProps when you need local and rest prop groupings without breaking reactivity.
  • Use mergeProps for layered defaults and overrides.
  • Prefer both over ad-hoc destructuring when props need to stay reactive.

Show

  • Use it for primary conditional UI branches with when={condition} and optional fallback={...}.
  • Prefer it over scattered && rendering and large ternaries.

For

  • Use it for referentially keyed list rendering with each={items()}.
  • Prefer it over .map(...) in dynamic UI code because it preserves identity and updates lists efficiently.

Index

  • Use it when the list shape is stable and per-index access matters more than keyed identity.
  • Do not replace every list with Index; choose it only when position-based retention is the real goal.

Suspense and ErrorBoundary

  • Pair Suspense with async reads such as createResource.
  • Use ErrorBoundary where async or rendering failures should be isolated.
  • Prefer these boundaries over hand-rolled loading, error, and ready booleans scattered through the tree.

lazy

  • Use it for component-level code splitting.
  • Lazy-load subtree components and usually render them under Suspense.

onCleanup

  • Use it to tear down timers, listeners, subscriptions, and external resources created in effects or component-owned scopes.
  • It prevents leaks and stale subscriptions.

untrack

  • Use it for intentionally non-reactive reads that should happen once without subscribing the current scope.
  • Use it narrowly. If a value should stay live, untrack is the wrong tool.

createContext and useContext

  • Use them for shared subtree state or capabilities that would otherwise require prop drilling.
  • Provide stable shared values near the subtree root and consume them with useContext.
  • Prefer context for intentional sharing, not as a blanket replacement for local state.

refs

  • Use refs for direct access to DOM nodes or child handles when imperative DOM work is actually needed.
  • They are appropriate for focus, measurement, and DOM-library integration.
  • Prefer normal declarative rendering unless imperative access is necessary.

Core best practices

Keep props and reads reactive

  • Pass values to JSX props unless the child intentionally accepts an accessor API.
  • Do not destructure reactive props by default; access them as props.name or preserve them with splitProps.
  • Keep reactive reads inside JSX, memos, or other reactive scopes. The component body is setup code, not a tracked render loop.

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>;
}

Prefer derivation over synchronization

  • If one value can be computed from another, derive it with a wrapper function or createMemo.
  • Use createMemo when the computation is reused, non-trivial, or clearly benefits from caching.
  • Do not mirror state through 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 () => ...).

Use the framework primitives that match the job

  • Prefer Show, For, Index, Switch, and Match over ad-hoc &&, ternaries, and .map(...) in JSX.
  • Prefer createStore when nested object shape and property-level updates matter.
  • Prefer splitProps and mergeProps when reshaping props.
  • Prefer context for intentional subtree sharing, not as a blanket replacement for local state.

Keep imperative work explicit

  • Use createEffect for DOM APIs, browser APIs, logging, subscriptions, and third-party integrations.
  • Use refs only when direct DOM or child-handle access is genuinely needed.
  • Pair long-lived imperative work with onCleanup.

If the job is ordinary rendering, keep it declarative. Use the rule packs below for testing, accessibility, web-component, and performance-specific guidance.

Debugging checklist

When a Solid 1.x component is not updating correctly, check these first:

  1. Was a signal or prop read outside a reactive scope?
  2. Were props destructured in a way that broke the getter?
  3. Was an accessor passed where a plain value should have been, or vice versa?
  4. Is createEffect being used to mirror state that should be derived?
  5. Should splitProps or mergeProps be used instead of destructuring?
  6. Would createStore better fit the shape of the state?
  7. Was untrack or batch used intentionally, or is it masking the real problem?

Specialized rule packs

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.

Control flow rules

RulePriorityDescription
3-1 Use Show for ConditionalsHIGHUse <Show> instead of ternary operators for primary conditional UI branches.
3-2 Use For for ListsHIGHUse <For> for referentially-keyed list rendering.
3-3 Use Index for PrimitivesMEDIUMUse <Index> when array position matters more than item identity.
3-4 Use Switch/MatchMEDIUMUse <Switch> and <Match> for multiple conditions instead of deeply nested ternaries.
3-5 Provide FallbacksLOWPrefer explicit fallback props for loading or empty states.
3-6 Stable Component MountMEDIUMAvoid rendering the same component in multiple Show or Switch branches; keep it in one stable position when possible.

State management rules

RulePriorityDescription
4-1 Signals vs StoresHIGHUse signals for primitives and simple references, stores for nested objects and arrays.
4-2 Use Store Path SyntaxHIGHUse store path syntax for granular, efficient updates.
4-3 Use produce for MutationsMEDIUMUse produce for complex mutable-style store updates.
4-4 Use reconcile for Server DataMEDIUMUse reconcile when integrating server or external data into an existing store shape.
4-5 Use Context for Global StateMEDIUMUse context for shared cross-component state or capabilities.

Refs and DOM rules

RulePriorityDescription
5-1 Use Refs CorrectlyHIGHUse callback refs for conditional elements and imperative handles that may appear later.
5-2 Access DOM in onMountHIGHAccess DOM elements in onMount, not during render.
5-3 Cleanup with onCleanupHIGHAlways clean up subscriptions, timers, listeners, and imperative integrations.
5-4 Use DirectivesMEDIUMUse use: directives for reusable element behaviors in Solid 1.x.
5-5 Avoid innerHTMLHIGHAvoid innerHTML with unsanitized content; prefer JSX or textContent.
5-6 Event Handler PatternsMEDIUMUse on: and oncapture: namespaces and array handler syntax correctly when the platform or library expects them.
5-7 Web Component Controlled StateHIGHUse createEffect, refs, and imperative calls to sync signals to web component APIs when declarative attributes are not enough.

Performance rules

RulePriorityDescription
6-1 Avoid Unnecessary TrackingHIGHDo not access signals outside reactive contexts unless the read is intentionally static.
6-2 Use Lazy ComponentsMEDIUMUse lazy() for code splitting when large components do not need to be in the initial bundle.
6-3 Use SuspenseMEDIUMUse <Suspense> for async loading boundaries instead of ad-hoc loading booleans everywhere.
6-4 Optimize Store AccessLOWRead only the store properties you actually need.
6-5 Prefer classListLOWUse classList for conditional class toggling in Solid 1.x.
6-6 Web Component CSS and Bundle StrategyMEDIUMImport custom elements individually when possible, and keep ::part() overrides in a global stylesheet rather than CSS modules.

Accessibility rules

RulePriorityDescription
7-1 Use Semantic HTMLHIGHUse the right semantic element before reaching for ARIA.
7-2 Use ARIA AttributesMEDIUMAdd appropriate ARIA attributes for custom controls and landmark exposure.
7-3 Support Keyboard NavigationMEDIUMMake sure interactive elements are reachable and usable from the keyboard.

Testing rules

RulePriorityDescription
8-1 Configure Vitest for SolidCRITICALConfigure Vitest with the Solid plugin and Solid-specific resolve conditions.
8-2 Wrap Render in Arrow FunctionsCRITICALAlways use render(() => <Comp />), not render(<Comp />).
8-3 Test Primitives in a RootHIGHWrap signal, memo, and effect tests in createRoot or use renderHook.
8-4 Handle Async in TestsHIGHUse findBy queries and correct timer configuration for async behavior.
8-5 Use Accessible QueriesMEDIUMPrefer role and label queries over test IDs.
8-6 Separate Logic from UI TestsMEDIUMTest reactive primitives independently from component rendering when possible.
8-7 Browser Mode for Web Components and PWA APIsHIGHUse Vitest browser mode for custom elements, shadow DOM, and browser-native APIs.
8-8 Testing Headless UI Libraries with Non-Standard ARIAMEDIUMInspect the real tree and portal structure before choosing queries.
8-9 Browser-Native API Test IsolationHIGHClear IndexedDB and localStorage between tests, and close connections before deleteDatabase.
8-10 Router Integration TestingHIGHUse MemoryRouter root setup to provide router context to layout and provider trees.
8-11 TanStack Query Test SetupHIGHCreate a fresh QueryClient per test with retries and caching disabled.

Task-focused rule selection

Writing new components

Load these rules first when creating new Solid 1.x components:

  • 1-1 Ensure signals are called as functions
  • 2-1 Prevent reactivity breakage from destructured props or early reads
  • 2-2 Handle default props correctly
  • 2-3 Separate local and forwarded props
  • 2-6 No early returns; prefer JSX control flow
  • 2-9 Never call components as plain functions
  • 3-1 Proper conditional rendering with Show
  • 3-2 Efficient list rendering with For
  • 5-3 Cleanup long-lived work with onCleanup

Code review

Bias code review toward these priorities:

  • CRITICAL: 1-1, 2-1, 2-6, 2-9
  • HIGH: 1-2, 1-3, 1-7, 2-7, 5-2, 5-3, 5-5

Performance optimization

Load these rules when optimizing performance:

  • 1-2 Prevent unnecessary recomputation
  • 1-6 Reduce update cycles
  • 4-2 Prefer granular store updates
  • 6-1 Prevent unwanted subscriptions
  • 6-2 Use code splitting when it meaningfully shrinks the initial bundle
  • 6-4 Read only the store properties that matter

State management

Load these rules when the task is mostly about application state:

  • 4-1 Choose the right primitive
  • 4-2 Use efficient store updates
  • 4-3 Use produce for complex mutations
  • 4-4 Use reconcile for external data integration
  • 4-5 Use context for cross-component state

Accessibility audit

Load these rules when auditing accessibility:

  • 7-1 Semantic structure
  • 7-2 Screen reader support
  • 7-3 Keyboard support

Writing tests

Load these rules when writing or reviewing tests:

  • 8-1 Correct Vitest configuration
  • 8-2 Reactive render scope
  • 8-3 Reactive ownership for primitives
  • 8-4 Async queries and timers
  • 8-5 Accessible query selection
  • 8-6 Test architecture separation
  • 8-7 Browser mode versus jsdom
  • 8-8 Portals and non-standard ARIA structures
  • 8-9 IndexedDB and localStorage cleanup patterns
  • 8-10 MemoryRouter setup for integration tests
  • 8-11 QueryClient configuration for tests

Integrating web components and custom elements

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:

  • 2-10 Declare custom element tags in the JSX namespace and type newer HTML attributes when TypeScript needs help
  • 5-6 Use on: for custom element events and type CustomEvent payloads correctly
  • 5-7 Sync Solid signals to imperative web component APIs with refs and effects
  • 6-6 Prefer per-component imports and keep ::part() overrides in global CSS

Common mistakes to catch

MistakeRuleSolution
Forgetting () on signal access1-1Always call signals like count().
Destructuring props2-1Access them via props.name or splitProps.
Using ternaries for primary conditionals3-1Prefer <Show>.
Using .map() for dynamic lists3-2Prefer <For>.
Deriving values in effects1-2Prefer createMemo or a derived function.
Setting signals in effects to mirror other state1-4Derive the value or trigger updates from the real source.
Accessing DOM during render5-2Use onMount.
Forgetting cleanup5-3Use onCleanup.
Early returns in components2-6Use <Show> or <Switch> in JSX.
Using className or htmlFor2-7Use class and for.
Using style="color: red" or camelCase inline styles2-8Use style={{ color: "red" }} and keep CSS property names platform-correct.
Using innerHTML with user data5-5Use JSX or sanitize with a library such as DOMPurify first.
Spreading a whole store when only one field is needed6-4Read specific properties.
String concatenation for conditional classes6-5Prefer classList={{ active: isActive() }}.
render(<Comp />) without an arrow8-2Use render(() => <Comp />).
Effects or memos in tests without an owner8-3Wrap them in createRoot or use renderHook.
getBy for async content8-4Use findBy.
Calling MyComp(props) instead of <MyComp />2-9Use JSX or createComponent().
Calling router hooks like useMatch() inside createEffect1-7Call hooks once at component init, not inside reactive computations.
Rendering the same component in multiple Switch branches3-6Keep it mounted in one stable position.
Custom elements not upgrading in tests8-7Use browser mode instead of jsdom.
IndexedDB state leaking between tests8-9Close the connection before deleteDatabase and reset state between tests.
Router primitives throwing missing-route errors8-10Provide router context with MemoryRouter.
Query retries masking failures in tests8-11Use a fresh QueryClient with retries disabled.
waitFor(length === 0) passing before data loads8-4Use a settled anchor like findBy before asserting absence.
getByRole('form') failing even though the form exists7-2Add aria-label or aria-labelledby so the form has an accessible name.
<my-element onMyChange={...}> missing custom events5-6Use on:my-change.
::part() rules inside CSS modules not applying6-6Move them to a global stylesheet.
Barrel imports for whole web component libraries6-6Import only the components you use.
value={signal()} on a custom element not syncing state5-7Listen for events and push values imperatively through the element ref.
<div popover> or <button popoverTarget="x"> TypeScript error2-10Augment the relevant HTMLElement types in a .d.ts file for newer HTML attributes.
Object props on custom elements becoming "[object Object]"5-7Use prop:myProp={value} when the library expects a JS property.
Experimental CSS properties such as anchor-name causing a TypeScript error2-8Cast through unknown to JSX.CSSProperties instead of forcing never.

React comparison

When the user is thinking in React terms, keep these comparisons handy:

ReactSolid.js 1.x
Components re-render on state changeComponents 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 freelyPreserve prop getters; do not destructure by default
Early returns like if (!x) return nullPrefer <Show> or <Switch> in JSX
{condition && <Comp />}<Show when={condition}><Comp /></Show>
{items.map(item => ...)}<For each={items}>{item => ...}</For>
classNameclass
htmlForfor
style={{ fontSize: 14 }}style={{ "font-size": "14px" }} when needed for CSS-value correctness
React 19 custom element events can look like onMyEventSolid uses on:my-event for custom element events

Priority levels

  • CRITICAL: fix immediately; likely runtime bugs, broken reactivity, or broken tests
  • HIGH: address in code review; correctness or maintainability issues
  • MEDIUM: apply when relevant; improves quality and ergonomics
  • LOW: refactor-level improvements and secondary optimizations

Tooling note

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.

Output expectations

When helping the user, prefer this structure:

  1. State the Solid-specific issue.
  2. Explain why it happens in Solid 1.x.
  3. Show the minimal corrected code.
  4. Mention any tradeoff only if it materially affects the choice.

Example prompts this skill should handle well

  • "Why does this Solid component stop updating when I destructure props?"
  • "Can you refactor this SolidJS component to be more idiomatic?"
  • "I think I am overusing createEffect in this Solid app."
  • "Review this Solid 1.x component and point out reactivity mistakes."

Additional resources

What ships with it: 1 file

1.7 KB alongside SKILL.md

evals/

Keep looking

Skills are one crate of 327,069. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.