A bunch of reactive utility types and functions, for building primitives with Solid.js
—
Core utilities for working with Solid.js reactivity system, including accessor manipulation, signal creation, and lifecycle management. These functions are designed to integrate seamlessly with Solid.js's reactive patterns and signal-based state management.
Functions for working with values that may or may not be accessors (reactive functions).
/**
* Accesses the value of a MaybeAccessor
* @param v - Value or accessor function to access
* @returns The accessed value
*/
function access<T extends MaybeAccessor<any>>(v: T): MaybeAccessorValue<T>;
/**
* Access an array of MaybeAccessors
* @param list - Array of values or accessor functions
* @returns Array of accessed values
*/
function accessArray<A extends MaybeAccessor<any>>(
list: readonly A[]
): MaybeAccessorValue<A>[];
/**
* Run the function if the accessed value is not undefined nor null
* @param value - MaybeAccessor to check and access
* @param fn - Function to run with the non-null value
*/
function withAccess<T, A extends MaybeAccessor<T>, V = MaybeAccessorValue<A>>(
value: A,
fn: (value: NonNullable<V>) => void
): void;
/**
* Convert MaybeAccessor to Accessor
* @param v - Value or accessor to convert
* @returns Accessor function
*/
function asAccessor<A extends MaybeAccessor<unknown>>(
v: A
): Accessor<MaybeAccessorValue<A>>;
/**
* If value is a function – call it with given arguments – otherwise get the value as is
* @param valueOrFn - Value or function to access
* @param args - Arguments to pass if valueOrFn is a function
* @returns Accessed value or function result
*/
function accessWith<T>(
valueOrFn: T,
...args: T extends AnyFunction ? Parameters<T> : never
): T extends AnyFunction ? ReturnType<T> : T;Usage Examples:
import { createSignal } from "solid-js";
import { access, accessArray, withAccess, asAccessor } from "@solid-primitives/utils";
// Basic access
const value = access("hello"); // "hello"
const signalValue = access(() => "world"); // "world"
// Array access
const values = accessArray([1, () => 2, 3]); // [1, 2, 3]
// Conditional access
withAccess(() => "value", (val) => {
console.log(val); // "value"
});
// Convert to accessor
const accessor = asAccessor("static value");
console.log(accessor()); // "static value"Enhanced signal creation functions with special capabilities like hydration support.
/**
* A hydratable version of createSignal. Uses serverValue on server and update function on client.
* During hydration uses serverValue as initial and updates once hydration is complete.
* @param serverValue - Initial value for server
* @param update - Function called on client to initialize value
* @param options - Signal options
* @returns Signal tuple [state, setState]
*/
function createHydratableSignal<T>(
serverValue: T,
update: () => T,
options?: SignalOptions<T>
): ReturnType<typeof createSignal<T>>;
/** @deprecated use createHydratableSignal instead */
const createHydrateSignal: typeof createHydratableSignal;Usage Examples:
import { createHydratableSignal } from "@solid-primitives/utils";
// Server-side rendering compatible signal
const [theme, setTheme] = createHydratableSignal(
"light", // Server default
() => localStorage.getItem("theme") || "light" // Client initialization
);
// With options
const [count, setCount] = createHydratableSignal(
0,
() => parseInt(sessionStorage.getItem("count") || "0"),
{ equals: false }
);Advanced effect utilities for handling delayed and conditional reactive updates.
/**
* Solid's `on` helper, but always defers and returns provided initial value instead of undefined
* @param deps - Dependencies to watch (accessor or array of accessors)
* @param fn - Effect function
* @param initialValue - Initial value to return
* @returns Effect function
*/
function defer<S, Next extends Prev, Prev = Next>(
deps: AccessorArray<S> | Accessor<S>,
fn: (input: S, prevInput: S, prev: undefined | NoInfer<Prev>) => Next,
initialValue: Next
): EffectFunction<undefined | NoInfer<Next>, NoInfer<Next>>;
function defer<S, Next extends Prev, Prev = Next>(
deps: AccessorArray<S> | Accessor<S>,
fn: (input: S, prevInput: S, prev: undefined | NoInfer<Prev>) => Next,
initialValue?: undefined
): EffectFunction<undefined | NoInfer<Next>>;Usage Examples:
import { createSignal, createEffect } from "solid-js";
import { defer } from "@solid-primitives/utils";
const [count, setCount] = createSignal(0);
createEffect(defer(
count,
(value, prevValue, prev) => {
console.log(`Count changed from ${prevValue} to ${value}`);
return value * 2;
},
0 // Initial value
));Functions for working with Solid.js lifecycle hooks and cleanup.
/**
* Solid's onCleanup that doesn't warn in development if used outside of a component
*/
const tryOnCleanup: typeof onCleanup;Utilities for managing and executing multiple callback functions.
/**
* Create a callback stack for managing multiple callbacks
* @returns Object with push, execute, and clear methods
*/
function createCallbackStack<A0 = void, A1 = void, A2 = void, A3 = void>(): {
push: (...callbacks: ((arg0: A0, arg1: A1, arg2: A2, arg3: A3) => void)[]) => void;
execute: (arg0: A0, arg1: A1, arg2: A2, arg3: A3) => void;
clear: VoidFunction;
};
/**
* Group synchronous function calls into microtasks
* @param fn - Function to group calls for
* @returns Grouped function
*/
function createMicrotask<A extends any[] | []>(fn: (...a: A) => void): (...a: A) => void;Usage Examples:
import { createCallbackStack, createMicrotask } from "@solid-primitives/utils";
// Callback stack
const stack = createCallbackStack<string>();
stack.push(
(msg) => console.log("First:", msg),
(msg) => console.log("Second:", msg)
);
stack.execute("Hello"); // Logs both messages and clears stack
// Microtask grouping
const logGrouped = createMicrotask((message: string) => {
console.log("Grouped:", message);
});
logGrouped("A");
logGrouped("B"); // Only the last call "B" will executeReactive-specific array manipulation and diffing utilities.
/**
* Handle items removed and added to the array by diffing by reference
* @param current - New array instance
* @param prev - Previous array copy
* @param handleAdded - Called for every added item
* @param handleRemoved - Called for every removed item
*/
function handleDiffArray<T>(
current: readonly T[],
prev: readonly T[],
handleAdded: (item: T) => void,
handleRemoved: (item: T) => void
): void;Usage Examples:
import { createSignal, createEffect } from "solid-js";
import { handleDiffArray } from "@solid-primitives/utils";
const [items, setItems] = createSignal<string[]>([]);
let prevItems: string[] = [];
createEffect(() => {
const currentItems = items();
handleDiffArray(
currentItems,
prevItems,
(item) => console.log("Added:", item),
(item) => console.log("Removed:", item)
);
prevItems = [...currentItems];
});type MaybeAccessor<T> = T | Accessor<T>;
type MaybeAccessorValue<T extends MaybeAccessor<any>> = T extends () => any ? ReturnType<T> : T;
type OnAccessEffectFunction<S, Prev, Next extends Prev = Prev> = (
input: AccessReturnTypes<S>,
prevInput: AccessReturnTypes<S>,
v: Prev
) => Next;
type AccessReturnTypes<S> = S extends MaybeAccessor<any>[]
? { [I in keyof S]: AccessReturnTypes<S[I]>; }
: MaybeAccessorValue<S>;Install with Tessl CLI
npx tessl i tessl/npm-solid-primitives--utils