npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

customizable-select

v0.5.0

Published

An accessible, dependency-free TypeScript replacement for enhanced select controls

Readme

customizable-select

An accessible, dependency-free TypeScript enhancement for native <select> and <input> elements. It provides searchable single and multiple selection, typed option metadata, native form integration, rendering hooks, and an optional React adapter.

customizable-select is a modern migration path from Select2, not a jQuery-compatible clone. The first release focuses on a reliable local-data core; remote data, pagination, tagging, and grouped options are deliberately reserved for future APIs.

Customizable Select demo

Features

  • Enhances native inputs and selects without replacing their form semantics.
  • Single and multiple selection with disabled and locked options.
  • Diacritic-insensitive local search and complete keyboard navigation.
  • Typed metadata, events, filters, and rendering hooks.
  • Configurable parsing and serialization for input-backed values.
  • Deterministic source synchronization and teardown restoration.
  • Optional controlled or uncontrolled React adapter.
  • Scoped CSS tokens with no JavaScript runtime dependencies.

Install

npm install customizable-select

Import the stylesheet once:

import 'customizable-select/styles.css';

Native Select

<label for="framework">Framework</label>
<select id="framework" name="framework"></select>
import {CustomizableSelect, type CustomizableSelectOption} from 'customizable-select';

type FrameworkData = {homepage: string};

const options: CustomizableSelectOption<FrameworkData>[] = [
	{id: 'react', text: 'React', data: {homepage: 'https://react.dev'}},
	{id: 'vue', text: 'Vue', data: {homepage: 'https://vuejs.org'}},
];

const select = new CustomizableSelect<FrameworkData>(document.querySelector('#framework')!, {
	options,
	placeholder: 'Choose a framework',
});

select.setValue('react');
console.log(select.getValue()); // "react"
console.log(select.getSelectedOptions()[0]?.data?.homepage);

The source element remains in the form and receives the selected value. User changes dispatch its normal bubbling change event.

Multiple Input

Inputs use comma-separated values by default. Supply parser and serializer functions when IDs can contain commas or another wire format is required.

const input = document.querySelector<HTMLInputElement>('#recipients')!;
const recipients = new CustomizableSelect(input, {
	options: users,
	multiple: true,
	parseInputValue: (value) => (value === '' ? [] : value.split('|')),
	serializeInputValue: (values) => values.join('|'),
});

Events

Use the typed instance API for component events and the source element for native form events:

select.addEventListener('customizable-select:change', ({detail}) => {
	console.log(detail.value, detail.values, detail.selectedOptions, detail.userInitiated);
});

Events include customizable-select:open, :close, :select, :unselect, :clear, and :change.

Programmatic setValue() calls are silent by default:

select.setValue('vue', {dispatchChange: true});

React

import {useState} from 'react';
import {CustomizableSelect} from 'customizable-select/react';
import 'customizable-select/styles.css';

export function FrameworkField() {
	const [value, setValue] = useState<string | null>('react');

	return (
		<CustomizableSelect
			name="framework"
			options={options}
			value={value}
			onValueChange={({value: nextValue}) => setValue(typeof nextValue === 'string' ? nextValue : null)}
		/>
	);
}

Use defaultValue for uncontrolled operation and sourceElement="input" for input serialization.

API Summary

  • open(), close(), focus()
  • setOptions(), getOptions()
  • setValue(), getValue(), getValues(), getValueString()
  • setSelectedOptions(), getSelectedOptions()
  • setMultiple(), setDisabled(), setReadOnly()
  • destroy()
  • createCustomizableSelect(), getCustomizableSelect(), destroyCustomizableSelect()

Read the complete guide for option semantics, accessibility, styling, lifecycle behavior, and Select2 migration guidance. Generated declarations and TypeDoc output are the authoritative API reference.

Development

pnpm install
pnpm run ci
pnpm run integration-test