
Security News
Deno Goes the Way of the Dinosaur
Cloudflare is acquiring Deno’s team, which will provide another year of bug fixes and security updates before ending support for the runtime.
@solid-primitives/utils
Advanced tools
A bunch of reactive utility types and functions, for building primitives
Solid Primitives Utilities is a support and helper package for a number of primitives in our library. Please free to augment or centralize useful utilities and methods in this package for sharing.
npm install @solid-primitives/utils
# or
pnpm add @solid-primitives/utils
# or
yarn add @solid-primitives/utils
Functional programming helpers for making non-mutating changes to data. Keeping it immutable. Useful for updating signals.
import { pick } from "@solid-primitives/utils/immutable";
const original = { foo: 123, bar: "baz" };
const newObj = pick(original, "foo");
original; // { foo: 123, bar: "baz" }
newObj; // { foo: 123 }
Use it for changing signals:
import { push, update } from "@solid-primitives/utils/immutable";
const [list, setList] = createSignal([1, 2, 3]);
setList(p => push(p, 4));
const [user, setUser] = createSignal({
name: "John",
street: { name: "Kingston Cei", number: 24 },
});
setUser(p => update(p, "street", "number", 64));
shallowArrayCopy - make shallow copy of an arrayshallowObjectCopy - make shallow copy of an objectshallowCopy - make shallow copy of an array/objectwithArrayCopy - apply mutations to the an array without changing the originalwithObjectCopy - apply mutations to the an object without changing the originalwithCopy - apply mutations to the an object/array without changing the originalpush - non-mutating Array.prototype.push()drop - non-mutating function that drops n items from the array startdropRight - non-mutating function that drops n items from the array endfilterOut - standalone Array.prototype.filter() that filters out passed itemfilter - standalone Array.prototype.filter()sort - non-mutating Array.prototype.sort() as a standalone functionsortBy - Sort an array by object key, or multiple keysmap - standalone Array.prototype.map() functionslice - standalone Array.prototype.slice() functionsplice - non-mutating Array.prototype.splice() as a standalone functionfill - non-mutating Array.prototype.fill() as a standalone functionconcat - Creates a new array concatenating array with any additional arrays and/or values.remove - Remove item from arrayremoveItems - Remove multiple items from an arrayflatten - Flattens a nested array into a one-level arrayfilterInstance - Flattens a nested array into a one-level arrayfilterOutInstance - Flattens a nested array into a one-level arrayomit - Create a new subset object without the provided keyspick - Create a new subset object with only the provided keyssplit - Split object into multiple subset objects.merge - Merges multiple objects into a single one.get - Get a single property value of an object by specifying a path to it.update - Change single value in an object by key, or series of recursing keys.add - a + b + c + ... (works for numbers or strings)substract - a - b - c - ...multiply - a * b * c * ...divide - a / b / c / ...power - a ** b ** c ** ...clamp - clamp a number value between two other values(string) => T transform functions for converting raw string data into typed values. Useful as the transform option for SSE, WebSocket, and similar streaming primitives.
import { json, ndjson, safe } from "@solid-primitives/utils";
const { data } = createSSE<Event>(url, { transform: json });
const { data } = createSSE<Event[]>(url, { transform: ndjson });
const { data } = createSSE<Event>(url, { transform: safe(json) });
json - Parse a string as a single JSON valuendjson - Parse newline-delimited JSON (NDJSON / JSON Lines) into an arraylines - Split a string into a string[] by newline, filtering empty linesnumber - Parse a string as a number via Number()safe(transform, fallback?) - Wrap any transform in a try/catch; returns fallback instead of throwingpipe(a, b) - Compose two transforms into oneIt is a typical use case to react on setting a new value; this is especially cumbersome for stores, where you otherwise need the deep package to make effects subscribe to all changes. A more performant and simple approach is to wrap the setter of your signal or store. To simplify this approach, we provide a wrapSetter function:
import { createStore } from "solid-js";
import { wrapSetter } from "@solid-primitives/utils";
const [state, setState] = wrapSetter(
createStore(
localStorage.getItem("persistedState")
? JSON.parse(localStorage.getItem("persistedState"))
: initialState,
),
setter => next => {
const output = setState(next);
localStorage.setItem(
"persistedState",
latest(() => JSON.stringify(state)),
);
return output;
},
);
If the signal or store is destructured into a tuple and augmented with additional values, those are left intact in the output. For the TS types to work, you need to as const the new tuple:
import { createSignal } from "solid-js";
import { wrapSetter } from "@solid-primitives/utils";
const augmentedSignal = [...createSignal(0), { extra: "data" }] as const;
const [count, setCount, data] = wrapSetter(
augmented,
setter => next => (console.log(next), setter(next)),
);
createSignal on the client; a plain mutable box on the server.
Solid 2 flags a setter that runs during a server render (SERVER_WRITE — the write is inert today and throws in a later release): server render is pure, state enters through async sources. Primitives that hold interactive state (a cursor, a toggle, a controlled value) have no async source, so on the server they keep a plain value instead. Reads and updater-form writes behave the same within a request; nothing touches the reactive graph. The signal is still created on the server so hydration ids stay aligned with the client — it is just never read or written there.
import { createServerSafeSignal } from "@solid-primitives/utils";
const [cursor, setCursor] = createServerSafeSignal<string | undefined>(undefined);
setCursor("b"); // a signal write in the browser, a plain assignment on the server
Multi-format color parsing, conversion, and accessibility naming — available as a separate subpath so you only pay for what you use.
Credits — The core color types, parsing, and color-space conversion logic (
types.ts,helpers.ts,intl.ts) are adapted from Kobalte (MIT, Copyright Fabien Marie-Louise), which itself credits the React Spectrum team (Apache 2.0, Copyright 2020 Adobe). The OKLCH inverse pipeline inmanipulation.tsuses coefficients from Björn Ottosson's OKLab work.
import {
parseColor,
normalizeColor,
getColorChannels,
normalizeHue,
} from "@solid-primitives/utils/colors";
import { COLOR_INTL_TRANSLATIONS } from "@solid-primitives/utils/colors";
const color = parseColor("#3264C8");
color.toString(); // "rgb(50, 100, 200)" (css default)
color.toString("hsl"); // "hsl(220 60% 49.02%)"
color.toString("hsb"); // "hsb(220 75% 78.43%)"
color.toString("hex"); // "#3264C8"
color.toString("hexa"); // "#3264C8FF"
// Convert to another space and inspect channels
const hsl = color.toFormat("hsl");
hsl.getChannelValue("hue"); // 220
hsl.getChannelValue("saturation"); // 60
hsl.getChannelValue("lightness"); // 49.02
// Derive a new color by changing one channel
const lighter = hsl.withChannelValue("lightness", 70);
lighter.toString("hex"); // "#6699E8"
Accepted input formats:
| Format | Examples |
|---|---|
| Hex | #rgb, #rgba, #rrggbb, #rrggbbaa |
| RGB | rgb(50 100 200), rgb(50, 100, 200), rgb(50 100 200 / 0.5), rgba(50, 100, 200, 0.5) |
| HSL | hsl(220, 60%, 49%), hsla(220, 60%, 49%, 0.5) |
| HSB | hsb(220, 75%, 78%), hsba(220, 75%, 78%, 0.5) |
parseColor throws for unrecognised strings. Use normalizeColor when your input may already be a Color object:
function accept(value: string | Color) {
const color = normalizeColor(value); // no-op if already a Color
}
// Ordered channel names for a space
getColorChannels("rgb"); // ["red", "green", "blue"]
getColorChannels("hsl"); // ["hue", "saturation", "lightness"]
getColorChannels("hsb"); // ["hue", "saturation", "brightness"]
// Range info for driving sliders
color.getChannelRange("red"); // { minValue: 0, maxValue: 255, step: 1, pageSize: 17 }
color.getChannelRange("alpha"); // { minValue: 0, maxValue: 1, step: 0.01, pageSize: 0.1 }
// Intl format options for display
color.getChannelFormatOptions("hue"); // { style: "unit", unit: "degree", unitDisplay: "narrow" }
// Locale-aware formatted value
color.toFormat("hsl").formatChannelValue("saturation"); // "60%"
// Axis layout for a 2D color area picker
color.getColorSpaceAxes({ xChannel: "saturation", yChannel: "brightness" });
// { xChannel: "saturation", yChannel: "brightness", zChannel: "hue" }
normalizeHue(370); // 10
normalizeHue(-10); // 350
normalizeHue(360); // 360 — kept distinct from 0 for full-circle sliders
getColorName converts to OKLCH — a perceptually uniform space — and produces a human-readable description suitable for aria-label and similar attributes. Pass COLOR_INTL_TRANSLATIONS for English or supply your own translations object.
import { parseColor, COLOR_INTL_TRANSLATIONS } from "@solid-primitives/utils/colors";
const t = COLOR_INTL_TRANSLATIONS;
parseColor("#3264C8").getColorName(t); // "dark vibrant blue"
parseColor("hsl(48 80% 55%)").getColorName(t); // "orange"
parseColor("rgb(50 100 200 / 0.2)").getColorName(t); // "dark vibrant blue, 80% transparent"
parseColor("#3264C8").getHueName(t); // "blue"
parseColor("#c86432").getHueName(t); // "brown"
// Channel label for a slider aria-label
parseColor("#fff").getChannelName("saturation", t); // "Saturation"
Providing translations for other locales:
import type { ColorIntlTranslations } from "@solid-primitives/utils/colors";
const frTranslations: ColorIntlTranslations = {
...COLOR_INTL_TRANSLATIONS,
hue: "Teinte",
saturation: "Saturation",
lightness: "Luminosité",
blue: "bleu",
// ...
};
tryParseColor("#3264C8"); // Color
tryParseColor("nope"); // undefined — never throws
isValidColor("#3264C8"); // true
isValidColor("nope"); // false
detectColorFormat("rgb(50, 100, 200)"); // "rgb"
detectColorFormat("#3264C8FF"); // "hexa"
detectColorFormat("hsla(220, 60%, 49%, 0.5)"); // "hsla"
detectColorFormat("nope"); // undefined
All manipulation functions return a new Color; the input is never mutated. amount is a 0–1 ratio representing percentage points on the relevant HSL channel (e.g. 0.1 = 10 pp).
import {
lighten,
darken,
saturate,
desaturate,
complement,
mix,
} from "@solid-primitives/utils/colors";
lighten(color, 0.1); // +10 lightness (HSL)
darken(color, 0.1); // −10 lightness (HSL)
saturate(color, 0.2); // +20 saturation (HSL)
desaturate(color, 0.2); // −20 saturation (HSL)
complement(color); // hue + 180° (HSL)
mix(black, white); // 50/50 blend in RGB
mix(black, white, 0.25); // 25% toward white
import { contrastRatio, isReadable } from "@solid-primitives/utils/colors";
contrastRatio(parseColor("#000"), parseColor("#fff")); // 21
contrastRatio(parseColor("#3264C8"), parseColor("#fff")); // ~3.9
// AA / AAA, normal / large text
isReadable(fg, bg); // AA normal (≥ 4.5 : 1)
isReadable(fg, bg, "AA", "large"); // AA large (≥ 3.0 : 1)
isReadable(fg, bg, "AAA"); // AAA normal (≥ 7.0 : 1)
isReadable(fg, bg, "AAA", "large"); // AAA large (≥ 4.5 : 1)
import { colorScale, perceptualColorScale } from "@solid-primitives/utils/colors";
// 5-step RGB gradient from black to white
colorScale(parseColor("#000"), parseColor("#fff"), 5);
// [#000000, #404040, #808080, #BFBFBF, #FFFFFF]
// 5-step perceptual gradient (OKLCH space, shortest hue arc)
// Red → Blue goes through purple, not green
perceptualColorScale(parseColor("hsl(0, 80%, 50%)"), parseColor("hsl(240, 80%, 50%)"), 5);
Parsing
parseColor(value) - Parse a color string; throws on invalid inputnormalizeColor(value) - Accept a string or Color, always return a ColortryParseColor(value) - Like parseColor but returns undefined instead of throwingisValidColor(value) - Boolean check without parsing overheaddetectColorFormat(value) - Detect the syntax format of a color stringColor space / channels
getColorChannels(space) - Ordered channel triple for a color spacenormalizeHue(hue) - Wrap a hue angle to [0, 360]colorToOKLCH(color) - Convert any Color to [L, C, H] in OKLCH spaceManipulation
lighten(color, amount) / darken(color, amount) - Adjust HSL lightnesssaturate(color, amount) / desaturate(color, amount) - Adjust HSL saturationcomplement(color) - Hue + 180°mix(a, b, ratio?) - Blend two colors in RGB spaceAccessibility
contrastRatio(a, b) - WCAG 2.1 contrast ratio (1–21)isReadable(fg, bg, level?, size?) - WCAG 2.1 AA / AAA compliance checkColor scales
colorScale(from, to, steps) - Linear RGB gradientperceptualColorScale(from, to, steps) - Perceptually uniform OKLCH gradienti18n / naming
COLOR_INTL_TRANSLATIONS - Default English translationsColorIntlTranslations (type) - Shape of a translations objectTypes
Color (interface) - The immutable color value typeColorFormat (type) - "hex" | "hexa" | "rgb" | "rgba" | "hsl" | "hsla" | "hsb" | "hsba"ColorSpace (type) - "rgb" | "hsl" | "hsb"ColorChannel (type) - "hue" | "saturation" | "brightness" | "lightness" | "red" | "green" | "blue" | "alpha"ColorAxes (type) - { xChannel, yChannel, zChannel: ColorChannel }ColorChannelRange (type) - { minValue, maxValue, step, pageSize }See CHANGELOG.md
FAQs
A bunch of reactive utility types and functions, for building primitives with Solid.js
The npm package @solid-primitives/utils receives a total of 0 weekly downloads. As such, @solid-primitives/utils popularity was classified as not popular.
We found that @solid-primitives/utils demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 3 open source maintainers collaborating on the project.

Security News
Cloudflare is acquiring Deno’s team, which will provide another year of bug fixes and security updates before ending support for the runtime.

Security News
Anthropic’s free OSS Scanner uses Mythos and other Claude models to find open source vulnerabilities and send maintainers reports with proposed fixes.

Security News
On Front Lines’ Early Adopters: CISO podcast, Andrew Becherer shares lessons from Datadog, weighs the long-term costs of AI-built tools, and explains how founders can actually gain a CISO’s trust.