Agent skill
solid-core-secondary-primitives
SolidJS secondary primitives: createComputed for immediate sync, createDeferred for idle updates, createReaction for one-time tracking, createRenderEffect for DOM operations, createSelector for optimized equality.
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/data/solid-core-secondary-primitives
Metadata
Additional technical details for this skill
- globs
-
[ "**/*computed*", "**/*deferred*", "**/*reaction*", "**/*selector*", "**/*render-effect*" ]
SKILL.md
SolidJS Secondary Primitives
Complete guide to advanced reactive primitives in SolidJS. These primitives provide fine-grained control over reactivity timing and execution.
createComputed - Immediate Synchronization
createComputed creates a reactive computation that runs before the rendering phase. Used to synchronize state before rendering begins.
import { createComputed } from "solid-js";
import { createStore } from "solid-js/store";
function UserEditor(props: { user: User }) {
const [formData, setFormData] = createStore({ name: "" });
// Update store synchronously when props change
// Prevents a second render cycle
createComputed(() => {
setFormData("name", props.user.name);
});
return (
<form>
<h1>Editing: {formData.name}</h1>
<input
value={formData.name}
onInput={(e) => setFormData("name", e.currentTarget.value)}
/>
</form>
);
}
Key characteristics:
- Runs before rendering phase
- Synchronous execution
- Prevents extra render cycles
- Receives previous value as argument
Use cases:
- Synchronizing props to local state
- Building custom primitives
- Immediate reactive updates
- State normalization
When to use vs createEffect:
createComputed: Need synchronous updates before rendercreateEffect: Need side effects after render completes
createDeferred - Idle Updates
createDeferred creates a readonly that only notifies downstream changes when the browser is idle. Optimizes performance by batching non-critical changes.
import { createDeferred, createSignal } from "solid-js";
function SearchComponent() {
const [searchTerm, setSearchTerm] = createSignal("");
// Defer updates until browser is idle
const deferredSearch = createDeferred(searchTerm, {
timeoutMs: 500, // Max wait time
});
// This only updates when browser is idle
return <div>Searching: {deferredSearch()}</div>;
}
Options:
timeoutMs: Maximum time to wait before forcing update (default: no timeout)equals: Custom equality functionname: Debug name
Use cases:
- Search input debouncing
- Non-critical UI updates
- Performance optimization
- Reducing render frequency
Example with timeout:
const deferred = createDeferred(source, {
timeoutMs: 1000, // Force update after 1 second even if not idle
});
createReaction - One-Time Tracking
createReaction separates tracking from execution. Registers a side effect that runs the first time the tracked expression changes.
import { createReaction, createSignal } from "solid-js";
function Component() {
const [s, set] = createSignal("start");
// Create reaction that runs once on first change
const track = createReaction(() => {
console.log("Something changed!");
});
// Track the signal
track(() => s());
// First change triggers reaction
set("end"); // "Something changed!"
// Second change doesn't trigger (reaction already ran)
set("final"); // No output
// Need to call track() again to re-enable
track(() => s());
set("again"); // "Something changed!"
}
Key characteristics:
- Runs once on first dependency change
- Separates tracking from execution
- Must call tracking function again to re-enable
Use cases:
- One-time initialization
- Custom tracking logic
- Advanced reactive patterns
- Separating concerns
Pattern: Lazy initialization:
const track = createReaction(() => {
initializeExpensiveOperation();
});
// Only initializes when first accessed
track(() => someSignal());
createRenderEffect - DOM Operations
createRenderEffect creates a reactive computation that runs synchronously during rendering, before elements are mounted.
import { createRenderEffect, createSignal } from "solid-js";
function Counter() {
const [count, setCount] = createSignal(0);
// Runs immediately during render phase
// Before elements are mounted
createRenderEffect(() => {
console.log("Count:", count());
// Can manipulate DOM here
});
return (
<div>
<p>Count: {count()}</p>
<button onClick={() => setCount(count() + 1)}>Increment</button>
</div>
);
}
Execution timing:
- Initial run: Synchronously during render, before mount, refs not set
- Subsequent runs: After pure computations, once per batch
- SSR: Runs once on server, then on client after hydration
Use cases:
- DOM manipulation during render
- Immediate effects
- Ref-independent effects
- Custom rendering logic
When to use vs createEffect:
createRenderEffect: Need immediate execution during rendercreateEffect: Need execution after render completes
Example: DOM manipulation:
function Component() {
const [value, setValue] = createSignal(0);
createRenderEffect(() => {
// Manipulate DOM immediately
document.title = `Count: ${value()}`;
});
return <div>{value()}</div>;
}
createSelector - Optimized Equality
createSelector creates a parameterized derived boolean signal optimized for selection states. Reduces updates from n to 2.
import { createSelector, For } from "solid-js";
function List() {
const [selectedId, setSelectedId] = createSignal<number>();
const isSelected = createSelector(selectedId);
return (
<For each={list()}>
{(item) => (
<li
classList={{ active: isSelected(item.id) }}
onClick={() => setSelectedId(item.id)}
>
{item.name}
</li>
)}
</For>
);
}
Performance benefit:
- Without selector: n DOM updates (one per item)
- With selector: 2 DOM updates (previous + current)
Custom equality:
const isSelected = createSelector(selectedId, (key, value) => {
// Custom comparison
return key === value.id;
});
Use cases:
- Selection states
- Active items
- Dropdown menus
- Tab navigation
- List highlighting
Example: Tab navigation:
function Tabs() {
const [activeTab, setActiveTab] = createSignal("home");
const isActive = createSelector(activeTab);
return (
<nav>
<For each={tabs}>
{(tab) => (
<button
classList={{ active: isActive(tab.id) }}
onClick={() => setActiveTab(tab.id)}
>
{tab.label}
</button>
)}
</For>
</nav>
);
}
Comparison Table
| Primitive | Timing | Use Case |
|---|---|---|
createComputed |
Before render | Synchronize state before render |
createDeferred |
Browser idle | Defer non-critical updates |
createReaction |
First change only | One-time side effects |
createRenderEffect |
During render | Immediate DOM operations |
createSelector |
On change | Optimized selection state |
When to Use Each
createComputed
- ✅ Synchronizing props to local state
- ✅ Building custom primitives
- ✅ Immediate state updates
createDeferred
- ✅ Search input debouncing
- ✅ Non-critical UI updates
- ✅ Performance optimization
createReaction
- ✅ One-time initialization
- ✅ Custom tracking patterns
- ✅ Lazy loading
createRenderEffect
- ✅ DOM manipulation during render
- ✅ Immediate effects
- ✅ Ref-independent operations
createSelector
- ✅ Selection states
- ✅ Active item tracking
- ✅ Performance-critical lists
Common Patterns
Synchronizing Props
function Editor(props: { data: Data }) {
const [local, setLocal] = createStore({});
// Sync props to local state before render
createComputed(() => {
setLocal(reconcile(props.data));
});
return <form>...</form>;
}
Debounced Search
function Search() {
const [query, setQuery] = createSignal("");
const deferredQuery = createDeferred(query, { timeoutMs: 300 });
// Only search when deferred updates
createEffect(() => {
search(deferredQuery());
});
return <input onInput={(e) => setQuery(e.target.value)} />;
}
Optimized List Selection
function ItemList() {
const [selected, setSelected] = createSignal<number>();
const isSelected = createSelector(selected);
return (
<For each={items()}>
{(item) => (
<div
classList={{ selected: isSelected(item.id) }}
onClick={() => setSelected(item.id)}
>
{item.name}
</div>
)}
</For>
);
}
One-Time Initialization
function Component() {
const [ready, setReady] = createSignal(false);
const track = createReaction(() => {
initializeExpensiveOperation();
setReady(true);
});
// Only initializes when first accessed
track(() => someCondition());
return <Show when={ready()}>Content</Show>;
}
Best Practices
-
Use createComputed for sync:
- When you need state synchronized before render
- Prevents extra render cycles
-
Use createDeferred for performance:
- Non-critical UI updates
- Search/filter inputs
- Large list rendering
-
Use createReaction sparingly:
- One-time side effects only
- Custom tracking patterns
- Advanced use cases
-
Use createRenderEffect for DOM:
- Immediate DOM operations
- Before mount effects
- Ref-independent code
-
Use createSelector for lists:
- Selection states
- Active items
- Performance-critical UI
Summary
- createComputed: Immediate sync before render
- createDeferred: Idle-time updates for performance
- createReaction: One-time tracking and execution
- createRenderEffect: Immediate effects during render
- createSelector: Optimized selection state (n → 2 updates)
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
Didn't find tool you were looking for?