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

react-creatable-select

v0.1.0

Published

Headless, searchable, creatable + editable select/combobox for React. Single & multi select, inline rename, optional delete, async-first. Built on Radix Popover, designed to drop into shadcn/ui.

Readme

react-creatable-select

Headless, searchable, creatable + editable select / combobox for React.

A logic-only combobox you style yourself. Built on Radix Popover with asChild on every part, so it drops straight into shadcn/ui — bring your own <Button>, <Input>, and icons. No CSS ships in the box.

  • Searchable — built-in filtering (or supply your own).
  • Creatable — inline "create new" flow.
  • Editable — inline rename of existing options (the pencil).
  • Single & multi-select — multiple flips value to an array; use ItemIndicator for the left-side checkmark.
  • Optional delete — opt in by passing onDelete.
  • Async-first — create / edit / delete callbacks may return Promises, with a shared status / error lifecycle.
  • Headless — a useCreatableSelect hook and asChild compound primitives.
  • RSC-safe — ships the "use client" directive; ESM + CJS + types.

Install

npm i react-creatable-select @radix-ui/react-popover

react, react-dom, and @radix-ui/react-popover are peer dependencies.

Two ways to use it

  1. <Select> — a batteries-included component with flat props (searchable / creatable / multiple / …), à la react-select. Headless (no CSS ships) but renders complete default DOM. Start here.
  2. CreatableSelect.* primitives (+ the useCreatableSelect hook) — the headless engine for full control over markup. Drop down when you outgrow <Select>.

Simplified API: <Select>

import { Select } from "react-creatable-select";

// Your data shape — no need for {value,label}; map it with getters.
const cases = [{ id: "1", name: "Case 1" }, { id: "2", name: "Case 2" }];

<Select
  options={cases}
  value={value}
  onChange={(v, option) => setValue(v)}
  getOptionValue={(c) => c.id}
  getOptionLabel={(c) => c.name}
  searchable          // show the search box + filter (default true)
  creatable           // show inline "create new" (default false)
  multiple            // multi-select; value becomes string[] (default false)
  clearable           // show a clear button (default false)
  placeholder="Select case"
  onCreate={(label) => api.createCase(label)}   // sync or async → returns your option
  onEdit={(opt, label) => api.renameCase(opt, label)}  // presence enables the pencil
  onDelete={(opt) => api.deleteCase(opt)}        // presence enables delete
/>;

No CSS ships. <Select> renders default DOM with stable rcs-* class hooks and the same data-* attributes as the primitives — style off those, pass a className (applied to the trigger) or a per-part classNames map, and override the built-in icons via the icons prop. The list is managed for you: options seeds the working list and create/edit/delete mutate a local copy (calling your async callbacks for persistence); pass a new options reference to resync.

See SelectProps for the full surface.

Quick start (primitives)

import { CreatableSelect } from "react-creatable-select";

<CreatableSelect.Root
  value={value}
  onValueChange={setValue}
  defaultOptions={[{ value: "1", label: "Case 1" }]}
  onCreate={(label) => api.createCase(label)}   // sync or async → returns the option
  onEdit={(opt, label) => api.renameCase(opt, label)}
>
  <CreatableSelect.Trigger asChild>
    <Button variant="outline"><CreatableSelect.Value placeholder="Select case" /></Button>
  </CreatableSelect.Trigger>

  <CreatableSelect.Content>
    <CreatableSelect.Search placeholder="Search" />

    <CreatableSelect.CreateTrigger>+ Create New</CreatableSelect.CreateTrigger>
    <CreatableSelect.CreateForm>
      <CreatableSelect.CreateInput placeholder="New case" />
      <CreatableSelect.CreateCommit>Create</CreatableSelect.CreateCommit>
      <CreatableSelect.CreateCancel>✕</CreatableSelect.CreateCancel>
    </CreatableSelect.CreateForm>

    {/* `options` is the live, filtered list — reflects search and runtime-created options. */}
    <CreatableSelect.List>
      {(options) => (
        <>
          <CreatableSelect.Empty>No results.</CreatableSelect.Empty>
          {options.map((o) => (
            <CreatableSelect.Item key={o.value} value={o.value}>
              <CreatableSelect.ItemIndicator><Check /></CreatableSelect.ItemIndicator>
              <CreatableSelect.ItemLabel />
              <CreatableSelect.ItemInput />             {/* shown while renaming */}
              <CreatableSelect.ItemEditTrigger><Pencil /></CreatableSelect.ItemEditTrigger>
            </CreatableSelect.Item>
          ))}
        </>
      )}
    </CreatableSelect.List>
  </CreatableSelect.Content>
</CreatableSelect.Root>

A full Tailwind/shadcn version matching the mockup (single and multi) lives in example/case-select.tsx.

Multi-select

<CreatableSelect.Root multiple value={values} onValueChange={setValues} ...>

value / selected become arrays, closeOnSelect defaults to false, and select toggles. Put <CreatableSelect.ItemIndicator> on the left of the item to render a checkmark only for selected rows.

Optional delete

Delete is off until you provide onDelete. Once you do, <CreatableSelect.ItemDeleteTrigger> renders (it returns null otherwise):

<CreatableSelect.Root onDelete={(opt) => api.deleteCase(opt)} ...>
  ...
  <CreatableSelect.ItemDeleteTrigger><Trash /></CreatableSelect.ItemDeleteTrigger>

Styling contract (data-*)

Style purely off attributes — never reach into internals:

| Attribute | On | Meaning | | --- | --- | --- | | data-state="open\|closed" | Trigger | popover open state | | data-highlighted | Item | keyboard/pointer highlight | | data-selected | Item | option is selected | | data-editing | Item | this row is being renamed | | data-state="checked\|unchecked" | ItemIndicator | selection (for gutter reservation) | | data-pending | CreateInput / CreateCommit / ItemInput | async mutation in flight |

[data-highlighted] { background: var(--accent); }
[data-selected]    { font-weight: 600; }

Hook-only usage

Skip the primitives entirely and drive your own markup:

const select = useCreatableSelect({ options, onCreate });
<input {...select.getSearchProps()} />
{select.filtered.map((o) => <li {...select.getItemProps(o)}>{o.label}</li>)}

See CreatableSelectController for the full surface (open, query, filtered, selected, mode, status, commitCreate, commitEdit, commitDelete, …).

License

MIT