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

forms-creatable-multi-select

v0.1.0

Published

A polished, accessible, creatable multi-select for React with async search, keyboard navigation, and themeable UI.

Readme

forms-creatable-multi-select

A polished, accessible, and highly customizable creatable multi-select for React. It works for both JavaScript and TypeScript projects, supports async search, lets users create missing options on the fly, and ships with npm-friendly build metadata.

Highlights

  • Multi-select with removable tags
  • Create-new flow with sync or async handlers
  • Async option loading with abort-aware search callbacks
  • Keyboard navigation and sensible accessibility defaults
  • Themeable via CSS variables
  • Customizable via classNames, styles, renderOption, and renderTag
  • Hidden input rendering for plain HTML form submission
  • Packaged for npm with ESM, CJS, CSS, and TypeScript declarations

Install

npm install forms-creatable-multi-select

Peer dependencies:

  • react >= 18
  • react-dom >= 18

Most modern bundlers will pick up the emitted CSS automatically from the package entry. If your setup wants it explicitly, import the stylesheet yourself:

import "forms-creatable-multi-select/styles.css";

Quick start (TypeScript)

import { useState } from "react";
import {
  CreatableMultiSelect,
  type CreatableOption,
} from "forms-creatable-multi-select";

const topicOptions: CreatableOption[] = [
  { value: "react", label: "React" },
  { value: "typescript", label: "TypeScript" },
  { value: "laravel", label: "Laravel" },
];

export function TopicPicker() {
  const [topics, setTopics] = useState<readonly CreatableOption[]>([]);

  return (
    <CreatableMultiSelect
      label="Topics"
      value={topics}
      onChange={setTopics}
      options={topicOptions}
      onCreateOption={async (inputValue) => ({
        value: inputValue.toLowerCase(),
        label: inputValue,
        __isNew__: true,
      })}
      helperText="Select existing topics or create your own."
    />
  );
}

Quick start (JavaScript)

import { useState } from "react";
import { CreatableMultiSelect } from "forms-creatable-multi-select";

const options = [
  { value: "critical", label: "Critical" },
  { value: "nice-to-have", label: "Nice to have" },
];

export default function PriorityPicker() {
  const [value, setValue] = useState([]);

  return (
    <CreatableMultiSelect
      label="Priority labels"
      value={value}
      onChange={setValue}
      options={options}
      placeholder="Pick labels or create one"
    />
  );
}

Async loading and create flow

const loadOptions = async (query: string, signal: AbortSignal) => {
  const response = await fetch(`/api/tags?q=${encodeURIComponent(query)}`, {
    signal,
  });

  const data = await response.json();

  return data.map((tag: { id: string; name: string }) => ({
    value: tag.id,
    label: tag.name,
  }));
};

const createTag = async (inputValue: string) => {
  const response = await fetch("/api/tags", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ name: inputValue }),
  });

  const tag = await response.json();

  return {
    value: tag.id,
    label: tag.name,
  };
};

<CreatableMultiSelect
  label="Tags"
  value={tags}
  onChange={setTags}
  options={[]}
  loadOptions={loadOptions}
  onCreateOption={createTag}
  loadDebounceMs={200}
/>;

Customization surfaces

Theme variables

Use the theme prop to override the built-in CSS variables without replacing the whole component style system.

<CreatableMultiSelect
  theme={{
    accent: "#7c3aed",
    accentSoft: "rgba(124, 58, 237, 0.15)",
    tagBackground: "#f5f3ff",
    tagText: "#5b21b6",
    radius: "28px",
  }}
  {...props}
/>

Slot classes and inline styles

<CreatableMultiSelect
  classNames={{
    control: "my-control",
    option: "my-option",
    footer: "my-footer",
  }}
  styles={{
    control: { minHeight: "4rem" },
    option: { borderRadius: "18px" },
  }}
  {...props}
/>

Custom renderers

<CreatableMultiSelect
  renderOption={(option, state) => (
    <div className="my-option-row">
      <strong>{option.label}</strong>
      <span>{state.isSelected ? "Selected" : option.description}</span>
    </div>
  )}
  renderTag={(option, state) => (
    <span className="my-chip">
      {option.label}
      <button type="button" onClick={state.onRemove}>
        ×
      </button>
    </span>
  )}
  {...props}
/>

Core props

| Prop | Type | Notes | | --- | --- | --- | | value | readonly CreatableOption[] | Controlled value | | defaultValue | readonly CreatableOption[] | Uncontrolled initial value | | options | readonly CreatableOption[] | Available choices | | onChange | (value, meta) => void | Called after select, remove, clear, or create | | onCreateOption | (input) => option \| Promise<option> | Return the created option to auto-select it | | loadOptions | (query, signal) => Promise<option[]> | Async search hook | | classNames | Partial<SlotClassNames> | Slot-level class overrides | | styles | Partial<SlotStyles> | Slot-level inline styles | | theme | Partial<ThemeVars> | CSS variable overrides | | renderOption | (option, state) => ReactNode | Custom menu row rendering | | renderTag | (option, state) => ReactNode | Custom selected-chip rendering | | footer | ReactNode \| (context) => ReactNode | Bottom menu content | | name | string | Renders hidden inputs for form posts |

Package scripts

From packages/creatable-multi-select:

npm run dev
npm run test
npm run typecheck
npm run build
npm run pack:preview
npm run publish:dry-run

Local demo

The package includes a Vite-powered demo app in src/demo/.

npm run dev

Publish checklist

  1. Update name, version, license, repository, and author metadata in package.json.
  2. Run typecheck, tests, and build.
  3. Inspect the tarball with npm run pack:preview.
  4. Validate publishing with npm run publish:dry-run.
  5. Publish with your preferred npm access and tag strategy.

Exported API

  • CreatableMultiSelect (default export and named export)
  • defaultTheme
  • Public TypeScript types such as CreatableOption, CreatableMultiSelectProps, ThemeVars, and related renderer metadata

Notes

  • The package is framework-agnostic beyond React itself; no Tailwind dependency is required.
  • The demo lives in the package so you can refine UI and public API together.
  • If you use server rendering, load the component only in environments where standard DOM interactions are available.