Select / MultiSelect

Wrappers around Mantine’s Select and MultiSelect, adding an optional “create new option” flow on top.

Import

import { Select, MultiSelect } from '@flxui/uikit'

Basic Usage

import { Select } from '@flxui/uikit'
 
function Demo() {
  return (
    <Select
      label="Status"
      data={[
        { value: 'active', label: 'Active' },
        { value: 'archived', label: 'Archived' }
      ]}
    />
  )
}

MultiSelect works the same way, just with an array value:

import { MultiSelect } from '@flxui/uikit'
 
function Demo() {
  return (
    <MultiSelect
      label="Tags"
      data={[
        { value: 'urgent', label: 'Urgent' },
        { value: 'bug', label: 'Bug' }
      ]}
    />
  )
}

Creatable

Set creatable to let users type something that isn’t in data and add it as a new option. onCreate is required whenever creatable is set:

import { useState } from 'react'
import { Select } from '@flxui/uikit'
 
function Demo() {
  const [data, setData] = useState([
    { value: 'design', label: 'Design' },
    { value: 'engineering', label: 'Engineering' }
  ])
 
  return (
    <Select
      label="Department"
      data={data}
      creatable
      onCreate={(query) => {
        const item = { value: query, label: query }
        setData((current) => [...current, item])
        return item
      }}
    />
  )
}
⚠️

Passing creatable without a valid onCreate function doesn’t fail gracefully — it throws at render time: Error: \onCreate` is required when `creatable` is true`. This will crash the component (and, depending on your error boundary setup, potentially the page) rather than just logging a warning.

⚠️

Returning null or undefined from onCreate doesn’t cleanly cancel the creation the way it looks like it should. When that happens, the component falls through to setting the raw internal placeholder string (something like "$create:yourquery") as the actual value — for Select, that string becomes value directly; for MultiSelect, it’s left inside the array. There’s currently no clean way to “reject” a create attempt from onCreate — always return a real ComboboxItem, or handle rejection in your own state before it reaches here.

searchable is forced on whenever creatable is set (searchable={false} is silently overridden) — typing is how the create flow works, so it can’t be turned off independently.

Custom getCreateLabel

Customize the text shown for the “create” option in the dropdown:

<Select
  creatable
  getCreateLabel={(query) => `Add "${query}" as new option`}
  onCreate={(query) => ({ value: query, label: query })}
  data={data}
/>

Default is + Create {query}.

Props

Both extend their respective Mantine props (SelectProps / MultiSelectProps) plus:

PropTypeDescription
creatablebooleanEnables the “create new option” flow
getCreateLabel(query: string) => stringLabel for the create option; defaults to + Create {query}
onCreate(query: string) => ComboboxItem | null | undefinedRequired if creatable is true. Return the item to create, or a falsy value (see warning above on what that actually does)
⚠️

Custom classNames you pass in are silently ignored on both components — Select/MultiSelect here always render with their own hardcoded classNames (input, error, dropdown, option, empty, groupLabel), set after your props are spread, so anything you pass in that prop has no effect. Use className for a root-level override, or reach for the underlying Mantine styling props/CSS variables instead.

Select’s displayed value is only ever a string or null — if the underlying value is anything else (e.g. left undefined or accidentally an array), the input just shows as empty rather than erroring. MultiSelect is more forgiving in the other direction: a single non-array truthy value gets auto-wrapped into a one-item array rather than rejected.