SolidJS logoSolidJSv1.9INTERMEDIATE

SolidJS

A comprehensive cheat sheet for SolidJS, the reactive JavaScript framework with fine-grained reactivity, signals, stores, and control flow components.

8 min read
solidjssolidreactivesignalsjavascriptfrontendframeworkui
Loading your progress

Components & JSX

SolidJS components are plain functions that run once. JSX compiles to real DOM operations, not a virtual DOM.

Components are functions that return JSX. They execute only once — reactivity handles updates.

tsx
// Quick Reference
const Greeting = (props) => {
  return <h1>Hello {props.name}</h1>;
};

// Usage: <Greeting name="SolidJS" />
💡 Components run only ONCE — unlike React, the function body is not re-executed on updates
⚡ Use class instead of className for HTML attributes in SolidJS JSX
📌 Do NOT destructure props — it breaks reactivity since props are lazy getters
🟢 SolidJS compiles JSX to real DOM operations, not a virtual DOM
componentsjsxbasics

Event Handling

Handle DOM events with on-prefixed attributes and native event delegation.

tsx
// Quick Reference
<button onClick={() => console.log("clicked")}>Click</button>
<input onInput={(e) => setValue(e.currentTarget.value)} />
💡 SolidJS uses event delegation for common events — they're handled at the document level
⚡ Use on: prefix (e.g., on:scroll) for events that should NOT be delegated
📌 Pass a tuple [handler, data] to bind data to an event without creating a closure
🟢 Events use native DOM types — e.currentTarget is properly typed in TypeScript
eventsdelegationhandlers

Signals

Signals are the core reactive primitive in SolidJS. They hold values and automatically track dependencies.

createSignal

Create a reactive signal that returns a getter function and a setter function.

typescript
// Quick Reference
import { createSignal } from "solid-js";

const [count, setCount] = createSignal(0);
console.log(count()); // 0
setCount(5);          // set directly
setCount(prev => prev + 1); // functional update
💡 Signals return getter functions — always call them with () to read the value
⚡ Setter accepts a direct value or a callback receiving the previous value
📌 Pass { equals: false } to always notify subscribers even if value is unchanged
🟢 Signals are the foundation of all reactivity in SolidJS
signalsstatereactivity

Derived Signals

Create computed values by wrapping signal reads in a function — no special API needed.

typescript
// Quick Reference
const [count, setCount] = createSignal(0);
const doubled = () => count() * 2;
// doubled() auto-updates when count changes
💡 Derived signals are plain functions — no API needed, just read signals inside a function
⚡ They re-evaluate on every access, which is fine for cheap computations
📌 For expensive computations, use createMemo instead for caching
🟢 This pattern is unique to SolidJS — no useMemo or computed() wrappers needed
signalsderivedcomputed

Control Flow

SolidJS uses components for control flow instead of JavaScript expressions to maintain fine-grained reactivity.

Show & For

Conditionally render with Show and efficiently iterate lists with For.

tsx
// Quick Reference
import { Show, For } from "solid-js";

<Show when={loggedIn()} fallback={<Login />}>
  <Dashboard />
</Show>

<For each={items()}>{(item) => <li>{item.name}</li>}</For>
💡 Show renders children when the "when" prop is truthy, fallback otherwise
⚡ For is keyed by reference — each item maintains its own DOM nodes across updates
📌 In For, the item is a plain value but the index is a signal — call index() to read it
🟢 Use For for arrays of objects, Index for arrays of primitives
showforconditionallist

Multi-condition rendering with Switch/Match and primitive-keyed iteration with Index.

tsx
// Quick Reference
import { Switch, Match, Index } from "solid-js";

<Switch fallback={<p>Not found</p>}>
  <Match when={route() === "home"}><Home /></Match>
  <Match when={route() === "about"}><About /></Match>
</Switch>
💡 Switch renders the first matching Match — use fallback for the default case
⚡ Index is keyed by position — use it for primitive arrays (strings, numbers)
📌 In Index, the item is a signal but the index is a plain number — opposite of For
🟢 For vs Index: For recycles DOM nodes by reference, Index recycles by position
switchmatchindexcontrol-flow

Effects & Memos

Effects run side effects in response to signal changes. Memos cache derived values for performance.

createEffect

Run a side effect that automatically re-executes when its signal dependencies change.

typescript
// Quick Reference
import { createSignal, createEffect } from "solid-js";

const [count, setCount] = createSignal(0);
createEffect(() => {
  console.log("Count is:", count());
});
💡 Effects auto-track dependencies — any signal called inside is a dependency
⚡ Effects run synchronously after render and re-run when dependencies change
📌 Return a value from the effect callback to access it as the previous value on next run
🟢 Use createEffect for side effects like logging, DOM manipulation, or external API calls
effectsreactivityside-effects

createMemo

Create a cached derived value that only recomputes when its dependencies change.

typescript
// Quick Reference
import { createSignal, createMemo } from "solid-js";

const [count, setCount] = createSignal(0);
const isEven = createMemo(() => count() % 2 === 0);
console.log(isEven()); // true
💡 createMemo caches its result — unlike derived signals, it only recomputes when dependencies change
⚡ Memos are themselves signals and can be tracked by effects and other memos
📌 Use memos for expensive computations or values read in multiple places
🟢 Think of createMemo as a derived signal with built-in caching
memocomputedcaching

Props Utilities

SolidJS props are reactive getters. Use mergeProps, splitProps, and children() to work with them safely.

Set default props with mergeProps and separate local from pass-through props with splitProps.

tsx
// Quick Reference
import { mergeProps, splitProps } from "solid-js";

// Default props
const merged = mergeProps({ color: "blue" }, props);

// Split props into groups
const [local, others] = splitProps(props, ["class", "style"]);
💡 Never destructure props — it reads the value once and loses reactivity
⚡ splitProps returns [picked, rest] — perfect for passing remaining props to DOM elements
📌 mergeProps creates a new reactive object with defaults that can be overridden
🟢 These utilities exist because destructuring breaks the reactive proxy
propsmergePropssplitProps

children Helper

Resolve and manipulate children reactively with the children() utility.

tsx
// Quick Reference
import { children } from "solid-js";
import type { ParentProps } from "solid-js";

const List = (props: ParentProps) => {
  const resolved = children(() => props.children);
  return <ul>{resolved()}</ul>;
};
💡 Use children() to resolve lazy children into a memoized reactive value
⚡ Call resolved.toArray() to get an array of child elements for inspection
📌 ParentProps<T> is a convenience type that adds children to your props interface
🟢 Only use children() when you need to inspect or transform children — otherwise just use props.children
childrenpropscomposition

Lifecycle

SolidJS lifecycle hooks for setup and cleanup within components.

Run code after the component mounts or when it unmounts.

typescript
// Quick Reference
import { onMount, onCleanup } from "solid-js";

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

onCleanup(() => {
  console.log("Component unmounted");
});
💡 onMount runs once after the component is first rendered to the DOM
⚡ onCleanup works in both components and effects — cleans up between re-runs too
📌 Since components run only once, you can set up intervals directly in the function body
🟢 onMount is equivalent to createEffect that runs only on first execution
lifecyclemountcleanup

Context

Share reactive state across the component tree without prop drilling.

Create and consume context to share state across deeply nested components.

tsx
// Quick Reference
import { createContext, useContext } from "solid-js";

const ThemeCtx = createContext("light");
// Provider: <ThemeCtx.Provider value="dark">
// Consumer: const theme = useContext(ThemeCtx);
💡 Context values can include signals and setters for reactive shared state
⚡ Create a custom useX hook that throws if context is missing for better DX
📌 Context is great for themes, auth, i18n, and other app-wide state
🟢 Unlike React, context consumers don't re-render — only the signal reads update
contextstate-sharingprovider

Stores

Stores provide reactive state for nested objects and arrays with fine-grained updates.

createStore

Create a reactive store for complex nested state with path-based updates.

typescript
// Quick Reference
import { createStore } from "solid-js/store";

const [state, setState] = createStore({ count: 0, user: { name: "Alice" } });
setState("count", 1);
setState("user", "name", "Bob");
💡 Store properties are accessed directly (state.count) — no getter function call needed
⚡ Path-based setState only triggers updates for the exact changed property
📌 Use a function as a path segment to match array items by condition
🟢 Stores are ideal for complex state — signals are better for simple atomic values
storesstatenested

Use produce for mutable-style updates and reconcile for replacing store data.

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

setState(produce(s => { s.user.name = "Bob"; }));
setState(reconcile(newData));
💡 produce lets you write mutable-looking code that produces immutable updates
⚡ reconcile diffs new data against the store to make minimal reactive updates
📌 Use reconcile when replacing store data from API responses for best performance
🟢 produce is imported from "solid-js/store", not from a separate package
storesproducereconcile

Refs & DOM Access

Access and manipulate DOM elements directly using refs and directives.

Get direct access to DOM elements using ref and forward refs to parent components.

tsx
// Quick Reference
let inputRef!: HTMLInputElement;
<input ref={inputRef} />
// Or with callback: <input ref={(el) => doSomething(el)} />
💡 Use the ! (definite assignment) operator since refs are assigned after render
⚡ Callback refs run immediately when the element is created — useful for canvas setup
📌 Access refs in onMount, not in the component body — the element may not exist yet
🟢 Forward refs by passing them as regular props — no special forwardRef API needed
refsdomelements

Resources & Data Fetching

createResource provides a reactive way to handle async data with built-in loading and error states.

createResource

Fetch async data reactively with automatic loading/error tracking and optional source signals.

tsx
// Quick Reference
import { createResource } from "solid-js";

const [data] = createResource(fetchData);
// data(), data.loading, data.error
💡 Pass a source signal as the first arg to auto-refetch when it changes
⚡ mutate() updates the local data optimistically without refetching
📌 refetch() manually triggers a new fetch — great for refresh buttons or polling
🟢 Combine with Suspense and ErrorBoundary for declarative loading states
resourceasyncfetching

Portals & Error Boundaries

Render content outside the component tree and handle errors declaratively.

Render components into a different DOM node and catch errors in the component tree.

tsx
// Quick Reference
import { Portal } from "solid-js/web";
import { ErrorBoundary, Suspense } from "solid-js";

<Portal><div class="modal">Modal Content</div></Portal>
<ErrorBoundary fallback={<p>Error!</p>}><App /></ErrorBoundary>
💡 Portal renders children into a target DOM element outside the component hierarchy
⚡ ErrorBoundary fallback receives the error and a reset function to retry rendering
📌 Combine ErrorBoundary with Suspense for complete async error and loading handling
🟢 Portal defaults to document.body — pass mount prop to target a specific element
portalerror-boundarysuspense

Reactivity Utilities

Advanced reactivity helpers for batching updates, untracking reads, and observing signals.

Control reactivity with batching, untracking, and explicit dependency declarations.

typescript
// Quick Reference
import { batch, untrack, on } from "solid-js";

batch(() => { setA(1); setB(2); }); // single update
const val = untrack(() => count()); // read without tracking
createEffect(on(count, (v) => console.log(v))); // explicit dep
💡 batch prevents intermediate updates — effects and memos update once after all changes
⚡ untrack reads the current signal value without creating a dependency
📌 on() explicitly declares dependencies — useful when you want to ignore some signals
🟢 Pass { defer: true } to on() to skip the initial execution of the effect
batchuntrackonreactivity