A collection of chainable and composable reactive signal calculations, aka Signal Builders.
| Stage | Category | Version | Last Updated | Demo |
|---|---|---|---|---|
| 3 | Reactivity | 1.0.0-next.6 (next) | Nov 12, 2026 | Demo → |
npm i @solid-primitives/signal-builders@nextA collection of chainable, composable reactive computations — Signal Builders — for common array, object, number, string, and type-conversion operations.
Usage
Each builder wraps its computation in createMemo, so results only update when the computed value actually changes. Builders must be called inside a reactive owner (a component body or createRoot), and computations should be kept pure — avoid side effects inside them.
Because each builder returns an Accessor<T>, the output of one can be passed directly as input to another:
Boolean
import { toggle } from "@solid-primitives/signal-builders";
const [isOpen, setIsOpen] = createSignal(false);const toggleOpen = toggle(setIsOpen);
toggleOpen(); // isOpen() === trueArray
import { push, flatten, remove } from "@solid-primitives/signal-builders";
const [fruits, setFruits] = createSignal(["apples", "bananas", "oranges", "tomatoes"]);const [toRemove, setToRemove] = createSignal("tomatoes");
const list = flatten(remove(push(fruits, ["kiwis", "avocados"]), toRemove));
list(); // ["apples", "bananas", "oranges", "kiwis", "avocados"]Object
import { update, merge } from "@solid-primitives/signal-builders";
const [user, setUser] = createSignal({ name: { first: "John", last: "Doe" } });const [last, setLast] = createSignal("Solid");
const modifiedUser = merge(update(user, "name", "last", last), { age: 21 });
modifiedUser(); // { name: { first: "John", last: "Solid" }, age: 21 }Number
import { add, multiply, clamp, int } from "@solid-primitives/signal-builders";
const [input, setInput] = createSignal("123");const [offset, setOffset] = createSignal(-45);const [max, setMax] = createSignal(1000);
const value = clamp(multiply(int(input), add(offset, 54, 9)), 0, max);String
import { lowercase, substring, template, add } from "@solid-primitives/signal-builders";
const [greeting, setGreeting] = createSignal("Hello");const [target, setTarget] = createSignal("World");
const message = template`${greeting}, ${target}!`;message(); // => "Hello, World!"
const solidMessage = lowercase(add(substring(message, 0, 7), "Solid"));solidMessage(); // => "hello, solid"Builder Reference
Boolean
toggle— wraps a boolean setter with a function that flips the current value
Array
push— append items to an arraydrop— remove n items from the startdropRight— remove n items from the endfilter—Array.prototype.filter()filterOut— remove all occurrences of a specific itemremove— remove the first occurrence of a specific itemremoveItems— remove multiple specific itemssplice—Array.prototype.splice()slice—Array.prototype.slice()map—Array.prototype.map()sort—Array.prototype.sort()concat— concatenate multiple arraysflatten— flatten one level of nestingfilterInstance— keep only items that are instances of the specified classesfilterOutInstance— remove items that are instances of the specified classes
push
Reactively appends items (signals or plain values) to an array, returning a new array.
filter
Reactive Array.prototype.filter() — the predicate may read signals.
sort
Reactive Array.prototype.sort() returning a sorted copy; the source array is not mutated.
map
Reactive Array.prototype.map() — the mapping function may read signals.
Object
omit— copy an object without the specified keyspick— copy an object with only the specified keysget— read a value at a key path (up to 6 levels deep)merge— shallow merge of multiple objectsupdate— immutably set a value at a key path; the last argument can be a new value or a setter function(prev) => next
get
Reactively reads a value at a key path (up to 6 levels deep).
merge
Reactively shallow-merges objects; later arguments override earlier ones.
update
Reactively and immutably sets a value at a key path; the last argument is a value, signal or (prev) => next setter.
Convert
string— convert a value to a stringfloat— parse a string as a float (Number.parseFloat)int— parse a string as an integer (Number.parseInt)join— join an array into a string with a separator
Number
add—a + b + c + ...substract—a - b - c - ...multiply—a * b * c * ...divide—a / b / c / ...power—a ** b ** c ** ...clamp— constrain a value between min and maxround—Math.round()ceil—Math.ceil()floor—Math.floor()
add
Reactive a + b + c + ... for numbers (or string concatenation).
clamp
Reactively constrains a value between min and max (inclusive).
String
lowercase—String.prototype.toLowerCase()uppercase—String.prototype.toUpperCase()capitalize— capitalize the first character and lowercase the restsubstring—String.prototype.substring()add—a + b + c + ...(string concatenation)template— reactive tagged template literal
template
Reactive tagged template literal; interpolations can be signals or plain values.
Changelog
See CHANGELOG.md