solid-core-utilities
SolidJS utilities: Context API, props manipulation (mergeProps/splitProps), JSX attributes (class/classList, events, refs), lifecycle (onMount/onCleanup), reactive utilities (batch/untrack/startTransition), store utilities.
How do I install this agent skill?
npx skills add https://smithery.ai --skill solid-core-utilitiesIs this agent skill safe to install?
No partner audit is available yet. Read the source before installing.
What does this agent skill do?
SolidJS Utilities
Context API
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>;
}
Props
- Props are reactive - access properties directly
- Use
mergePropsto combine props with defaults - Use
splitPropsto separate props - Props with
defaultorinitialprefix are common for one-time initialization; the linter allows this pattern without explicituntrack
import { 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)} />;
}
children Helper
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:
- Accessing
props.childrenmultiple times - Accessing
props.childrenoutside tracking scope - Resolving/transforming children before use
Important: 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
JSX Attributes
class and classList
// 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()} />
Event Handlers
// 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} />
Other Attributes
// 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} />
Refs and Directives
- Use
refattribute to access DOM elements directly refcan be a variable or a callback function- In TypeScript, use
let myElement!: HTMLDivElement;for definitive assignment - Signal setters or callbacks can be used as refs for dynamic elements
- Forward refs by passing
props.refto child's element - Directives (
use:) attach reusable behaviors to DOM elements- Function signature:
(element: Element, accessor: () => any): void - Can have multiple directives on an element
- Can pass reactive data to accessor
- Function signature:
import { 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>;
Lifecycle
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();
});
}
Reactive Utilities
batch
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
});
untrack
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>;
}
startTransition
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);
});
from
Convert promises/observables to signals:
import { from } from "solid-js";
const data = from(fetch("/api/data").then(r => r.json()));
mapArray / indexArray
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);
on
Execute effect when specific signal changes:
import { on } from "solid-js";
createEffect(on(count, (value) => {
console.log("Count changed to:", value);
}, { defer: true }));
catchError
Catch errors in reactive contexts:
import { catchError } from "solid-js";
const data = catchError(() => {
return riskyOperation();
}, (err) => {
console.error("Error:", err);
return defaultValue;
});
Store Utilities
produce
Mutate store data imperatively:
import { produce } from "solid-js/store";
setStore("user", produce(user => {
user.name = "New Name";
user.age = 31;
}));
reconcile
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
unwrap
Get plain object snapshot:
import { unwrap } from "solid-js/store";
const plainData = unwrap(store); // Plain JS object
// Use for serialization, API calls, etc.
createMutable
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
Advanced Patterns
createResource
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>
);
}
Portals
Renders children into a different DOM node:
import { Portal } from "solid-js/web";
<Portal mount={document.body}>
<Modal>Content</Modal>
</Portal>
Lazy Components
import { lazy, Suspense } from "solid-js";
const LazyComponent = lazy(() => import("./Component"));
<Suspense fallback={<Loading />}>
<LazyComponent />
</Suspense>
Best Practices
- Create custom context hooks for type safety and error handling
- Use
mergePropsfor default props - Use
splitPropsto separate local and forwarded props - Use
batchwhen making multiple related updates - Use
untrackto read values without tracking - Use
producefor complex store mutations - Use
reconcilewhen merging fetched data - Use
unwrapwhen serializing or sending to API
How can the creator link this skill?
Add the canonical catalog link to the repository README so users can inspect current installs and available audits. The publishing guide covers the complete discovery path.
<a href="https://skillzs.dev/skills/smithery.ai/solid-core-utilities">View solid-core-utilities on skillZs</a>