New:Introducing Socket Scanning for VS Code Marketplace Extensions.Learn more →
Get Started

@solid-primitives/controlled-signal

Package Overview
Dependencies
Maintainers
3
Versions
5
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@solid-primitives/controlled-signal

Reactive signals that support both controlled (externally managed) and uncontrolled (internally managed) state — a pattern commonly used in headless UI components.

Source
npmnpm
Version
1.0.0-next.3
Version published
Weekly downloads
0
Maintainers
3
Weekly downloads
 
Created
Source

@solid-primitives/controlled-signal

size version stage tested with vitest

Reactive signals that support both controlled (externally managed) and uncontrolled (internally managed) state — a pattern commonly used in headless UI components such as dialogs, dropdowns, and toggles.

Installation

npm install @solid-primitives/controlled-signal
# or
yarn add @solid-primitives/controlled-signal
# or
pnpm add @solid-primitives/controlled-signal

How it works

A controllable signal has two modes:

  • Uncontrolled — when no value prop is provided (or it is undefined). The signal manages its own internal state, initialized from defaultValue.
  • Controlled — when a value prop is provided. The signal defers entirely to the external value; setValue calls onChange but does not update internal state.

This mirrors the controlled/uncontrolled pattern from React, adapted for SolidJS reactivity.

Primitives

createControllableSignal

import { createControllableSignal } from "@solid-primitives/controlled-signal";

const [value, setValue] = createControllableSignal<T>(props);

Props:

PropTypeDescription
valueAccessor<T | undefined>Controlled value. When defined, enables controlled mode.
defaultValueAccessor<T | undefined>Initial value for uncontrolled mode.
onChange(value: T) => voidCalled whenever the value would change.

Returns: [value: Accessor<T | undefined>, setValue: (next) => void]

Uncontrolled usage

const [open, setOpen] = createControllableSignal({ defaultValue: () => false });

setOpen(true); // updates internal state and calls onChange if provided
setOpen(prev => !prev); // functional form

Controlled usage

// The consumer drives the state; your component just calls onChange.
const [value, setValue] = createControllableSignal({
  value: () => props.open,
  onChange: props.onOpenChange,
});

// setValue does NOT update internal state in controlled mode.
// It calls onChange so the consumer can update their signal.
setValue(true);

createControllableBooleanSignal

Variant of createControllableSignal for boolean values. Returns false instead of undefined when unset.

const [open, setOpen] = createControllableBooleanSignal({
  defaultValue: () => false,
});

createControllableArraySignal

Variant for Array<T> values. Returns [] instead of undefined when unset.

const [items, setItems] = createControllableArraySignal<string>({
  defaultValue: () => ["a", "b"],
});

setItems(prev => [...prev, "c"]);

createControllableSetSignal

Variant for Set<T> values. Returns new Set() instead of undefined when unset.

const [selected, setSelected] = createControllableSetSignal<number>({
  defaultValue: () => new Set([1, 2]),
});

setSelected(prev => new Set([...prev, 3]));

createToggleState

Controllable state for toggle components — checkboxes, switches, toggle buttons — built on top of createControllableBooleanSignal. Adapted from Kobalte's createToggleState.

import { createToggleState } from "@solid-primitives/controlled-signal";

const { isSelected, setIsSelected, toggle } = createToggleState({
  defaultIsSelected: () => false,
});

toggle(); // isSelected() === true

Props:

PropTypeDescription
isSelectedAccessor<boolean | undefined>Controlled selected state. When defined, enables controlled mode.
defaultIsSelectedAccessor<boolean | undefined>Initial selected state for uncontrolled mode.
isDisabledAccessor<boolean | undefined>While true, toggle() and setIsSelected() are no-ops.
isReadOnlyAccessor<boolean | undefined>While true, toggle() and setIsSelected() are no-ops.
onSelectedChange(isSelected: boolean) => voidCalled whenever the selected state would change.

Returns: { isSelected: Accessor<boolean>, setIsSelected: (v: boolean) => void, toggle: () => void }

Just like createControllableSignal, isSelected can be omitted for uncontrolled usage, or provided (alongside onSelectedChange) to let a parent component drive the state.

Credits

This primitive is adapted from the create-controllable-signal and create-toggle-state implementations in Kobalte by the Kobalte Contributors, used under the MIT License.

Changelog

See CHANGELOG.md

Keywords

solid

FAQs

Package last updated on 12 Aug 2026

Related posts