SolidJS utilities: Context API, props manipulation (mergeProps/splitProps), JSX attributes (class/classList, events, refs), lifecycle (onMount/onCleanup), reactive utilities...
Use createContext to create context. Provider component wraps children and passes value. useContext consumes context.
import { createContext, useContext, createSignal } from "solid-js";
// Create context
const ThemeContext = createContext();
// Provider with signal
function ThemeProvider(props) {
const [theme, setTheme] = createSignal("light");
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
{props.children}
</ThemeContext.Provider>
);
}
// Custom hook with error handling
function useTheme() {
const ctx = useContext(ThemeContext);
if (!ctx) throw new Error("useTheme must be used within ThemeProvider");
return ctx;
}
// Use in component
function Component() {
const { theme, setTheme } = useTheme();
return <div class={theme()}>Content</div>;
}
mergeProps to combine props with defaultssplitProps to separate propsdefault or initial prefix are common for one-time initialization; the linter allows this pattern without explicit untrackimport { mergeProps, splitProps } from "solid-js";
// Merge props
function Button(props) {
const merged = mergeProps({ type: "button" }, props);
return <button {...merged}>Click</button>;
}
// Split props
function Component(props) {
const [local, others] = splitProps(props, ["name", "age"]);
return <div {...others}>{local.name}</div>;
}
// Default/initial props (no tracking needed)
function Input(props) {
const [value, setValue] = createSignal(props.initialValue);
return <input value={value()} onInput={e => setValue(e.target.value)} />;
}
Use children helper for complex child manipulation. Prevents duplicate DOM creation.
import { children } from "solid-js";
function Wrapper(props) {
// Resolve children once (prevents duplicate DOM)
const resolved = children(() => props.children);
// Reuse resolved children multiple times
return (
<div>
{resolved()}
<div>Duplicate: {resolved()}</div>
</div>
);
}
When to use:
props.children multiple timesprops.children outside tracking scopeImportant: children helper evaluates immediately - use conditionally for conditional rendering:
// ❌ Always evaluates
const resolved = children(() => props.children);
<Show when={visible()}>{resolved()}</Show>
// ✅ Conditional evaluation
const resolved = children(() => visible() && props.children);
Array normalization:
const resolved = children(() => props.children);
const array = resolved.toArray(); // Normalize to array
// Static class
<div class="active" />
// Dynamic class
<div class={isActive() ? "active" : undefined} />
// classList object
<div
classList={{
active: isActive(),
disabled: isDisabled(),
"large-text": size() === "large"
}}
/>
// Signal classList
const [classes, setClasses] = createSignal({});
<div classList={classes()} />
// onClick, onInput, etc. (lowercase 'on')
<button onClick={handleClick}>Click</button>
<input onInput={e => console.log(e.target.value)} />
// Event delegation with on:eventName
<button on:click={handler} />
<div on:mousedown={handler} />
// ref callback
let inputEl;
<input ref={inputEl} />
// style object (reactive)
<div style={{ color: color(), fontSize: "14px" }} />
// innerHTML (dangerous, use carefully)
<div innerHTML={htmlContent()} />
// use:* directives
<div use:someDirective={arg} />
ref attribute to access DOM elements directlyref can be a variable or a callback functionlet myElement!: HTMLDivElement; for definitive assignmentprops.ref to child's elementuse:) attach reusable behaviors to DOM elements(element: Element, accessor: () => any): voidimport { createSignal, Show } from "solid-js";
// Basic ref
let myInput!: HTMLInputElement;
<input ref={myInput} />;
// Callback ref
let myDiv;
<div ref={(el) => (myDiv = el)}>Content</div>;
// Signal as ref
const [show, setShow] = createSignal(false);
let element!: HTMLParagraphElement;
<Show when={show()}>
<p ref={element}>This is the ref element</p>
</Show>;
// Forwarding refs
function ChildComponent(props) {
return <input ref={props.ref} />;
}
function ParentComponent() {
let inputRef;
return <ChildComponent ref={inputRef} />;
}
// Custom directive
function myDirective(element: Element, accessor: () => any) {
createEffect(() => {
element.textContent = accessor();
});
}
<div use:myDirective={mySignal()}></div>;
import { onMount, onCleanup, createSignal } from "solid-js";
function Component() {
const [data, setData] = createSignal(null);
// Runs once when component mounts
onMount(async () => {
const fetchedData = await fetchData();
setData(fetchedData);
});
// Runs when component unmounts or effect re-runs
onCleanup(() => {
// Cleanup subscriptions, timers, etc.
subscription.unsubscribe();
});
}
Batch multiple updates - effects run once at the end:
import { batch } from "solid-js";
const [up1, setUp1] = createSignal(1);
const [up2, setUp2] = createSignal(2);
const [up3, setUp3] = createSignal(3);
const down = createMemo(() => up1() + up2() + up3());
createEffect(() => console.log("Downstream:", down()));
// Batch multiple updates
batch(() => {
setUp1(10);
setUp2(10);
setUp3(10);
// All updates processed together, 'down' recomputes once
});
Access signal without tracking:
import { untrack } from "solid-js";
// Access signal without tracking
const value = untrack(() => props.staticValue);
// Props with default/initial prefix are auto-untracked
function MyComponent(props: { initialName: string }) {
const [name, setName] = createSignal(props.initialName); // ✅ OK
return <div>{name()}</div>;
}
Mark non-urgent updates:
import { startTransition } from "solid-js";
const [filter, setFilter] = createSignal("");
const [search, setSearch] = createSignal("");
startTransition(() => {
// Non-urgent updates
setFilter(filterValue);
setSearch(searchValue);
});
Convert promises/observables to signals:
import { from } from "solid-js";
const data = from(fetch("/api/data").then(r => r.json()));
Transform arrays with fine-grained reactivity:
import { mapArray, indexArray } from "solid-js";
// Map by item identity
const mapped = mapArray(items, item => item.id);
// Map by index
const indexed = indexArray(items, item => item.id);
Execute effect when specific signal changes:
import { on } from "solid-js";
createEffect(on(count, (value) => {
console.log("Count changed to:", value);
}, { defer: true }));
Catch errors in reactive contexts:
import { catchError } from "solid-js";
const data = catchError(() => {
return riskyOperation();
}, (err) => {
console.error("Error:", err);
return defaultValue;
});
Mutate store data imperatively:
import { produce } from "solid-js/store";
setStore("user", produce(user => {
user.name = "New Name";
user.age = 31;
}));
Merge new data efficiently (only updates changed fields):
import { reconcile } from "solid-js/store";
const newData = fetchNewData();
setStore("items", reconcile(newData)); // Only updates changed items
Get plain object snapshot:
import { unwrap } from "solid-js/store";
const plainData = unwrap(store); // Plain JS object
// Use for serialization, API calls, etc.
Create mutable store (use sparingly):
import { createMutable } from "solid-js/store";
const state = createMutable({ count: 0 });
// Direct mutation
state.count++;
// No setter needed, but less reactive control
Manages asynchronous data fetching:
import { createResource, Suspense } from "solid-js";
async function fetchUser(id: string) {
const response = await fetch(`/api/users/${id}`);
return response.json();
}
function DataComponent(props: { userId: string }) {
const [data, { mutate, refetch }] = createResource(
() => props.userId,
fetchUser,
{
initialValue: null,
name: "user"
}
);
return (
<Suspense fallback={<div>Loading user...</div>}>
<div>{data()?.name}</div>
<button onClick={() => refetch()}>Refetch</button>
</Suspense>
);
}
Renders children into a different DOM node:
import { Portal } from "solid-js/web";
<Portal mount={document.body}>
<Modal>Content</Modal>
</Portal>
import { lazy, Suspense } from "solid-js";
const LazyComponent = lazy(() => import("./Component"));
<Suspense fallback={<Loading />}>
<LazyComponent />
</Suspense>
mergeProps for default propssplitProps to separate local and forwarded propsbatch when making multiple related updatesuntrack to read values without trackingproduce for complex store mutationsreconcile when merging fetched dataunwrap when serializing or sending to API