SelectArrayInput

Multi-select dropdown input for choosing several values from a static list. The trigger button displays the selected values as small badges; clicking it opens a popover with a checkbox row for each choice.

Installation

pnpm dlx shadcn@latest add @shadmin/select-array-input

Usage

In addition to the source, <SelectArrayInput> requires one prop: the choices listing the possible values.

import { SelectArrayInput } from "@/components/admin";
 
<SelectArrayInput
  source="tags"
  choices={[
    { id: "tech", name: "Tech" },
    { id: "lifestyle", name: "Lifestyle" },
    { id: "people", name: "People" },
  ]}
/>;

By default, the possible choices are built from the choices prop, using:

  • the id field as the option value,
  • the name field as the option text.

The form value for the source must be an array of selected ids, e.g.:

const record = {
  id: 1,
  tags: ["tech", "people"],
};

Props

PropRequiredTypeDefaultDescription
sourceRequired*string-Field name (inferred in ReferenceArrayInput)
choicesRequired*Object[]-List of items. Required if not inside a ReferenceArrayInput.
classNameOptionalstring-Wrapper classes
createOptionalReactElement-React element rendered as an inline-create entry at the bottom of the menu
createHintValueOptionalstring-Internal sentinel placeholder shown in the input while the create element is active
createItemLabelOptionalstringra.action.create_itemLabel for the create menu item when the filter is non-empty
createLabelOptionalstringra.action.createHint label for the create menu entry when the filter is empty
createValueOptionalstring@@ra-createSentinel value that tags the create option in the options list
InputLabelPropsOptionalComponentProps<typeof FormLabel>-Props forwarded directly to the label element above the trigger
onCreateOptional(filter?: string) => Promise<{ id, name }>-Lightweight callback to create a new choice without a full dialog
defaultValueOptionalany[][]Default value
disabledOptionalboolean-Disable input
disableValueOptionalstringdisabledField marking disabled choices
formatOptionalfunction-Callback to convert the value from the form state into the input value
helperTextOptionalReactNode-Help text
labelOptionalstring | ReactNode | falseInferredCustom / hide label
optionTextOptionalstring | Function | ComponentnameField name of the record to display as the option text, or function/component that renders it
optionValueOptionalstringidField name of the record containing the value to use as the form value
parseOptionalfunction-Callback to convert the input value into the value stored in the form state
placeholderOptionalstring-Text shown in the trigger button when no value is selected
translateChoiceOptionalbooleantrueWhether the choices should be translated
validateOptionalValidator | Validator[]-Validation

* source and choices are optional inside <ReferenceArrayInput>.

choices

The list of choices must be an array of objects with at least two fields: one to use for the name, and the other to use for the value. By default, <SelectArrayInput> will use the id and name fields.

const choices = [
  { id: "tech", name: "Tech" },
  { id: "lifestyle", name: "Lifestyle" },
  { id: "people", name: "People" },
];
<SelectArrayInput source="tags" choices={choices} />;

If the choices have different keys, use optionText and optionValue:

const choices = [
  { _id: "tech", label: "Tech" },
  { _id: "lifestyle", label: "Lifestyle" },
  { _id: "people", label: "People" },
];
 
<SelectArrayInput
  source="tags"
  choices={choices}
  optionText="label"
  optionValue="_id"
/>;

The choices are translated by default, so you can use translation identifiers as choices:

const choices = [
  { id: "tech", name: "myroot.tags.tech" },
  { id: "lifestyle", name: "myroot.tags.lifestyle" },
  { id: "people", name: "myroot.tags.people" },
];

You can opt out of this translation by setting the translateChoice prop to false.

To fetch the options from another resource, wrap <SelectArrayInput> in a <ReferenceArrayInput>:

<ReferenceArrayInput source="tag_ids" reference="tags">
  <SelectArrayInput />
</ReferenceArrayInput>

placeholder

Text displayed in the trigger button when no value is selected. Defaults to a blank space so the button keeps a consistent height.

<SelectArrayInput
  source="tags"
  choices={choices}
  placeholder="Pick one or more tags..."
/>

disableValue

You can disable some choices by setting a disabled field to true (or by passing a custom field name with the disableValue prop):

const choices = [
  { id: "tech", name: "Tech" },
  { id: "lifestyle", name: "Lifestyle" },
  { id: "people", name: "People", disabled: true },
];
<SelectArrayInput source="tags" choices={choices} />;

onCreate

Lightweight callback alternative to create. Called with the current filter string when the user clicks the create menu entry. Return a new record (must include id and the field used by optionText) and the component appends its id to the selected values automatically.

Use onCreate for simple one-step creation (e.g. a prompt() or a quick API call). For complex creation UI, use create instead.

<SelectArrayInput
  source="tags"
  choices={choices}
  onCreate={async (filter) => {
    const newTag = await dataProvider.create("tags", {
      data: { name: filter },
    });
    return newTag.data;
  }}
/>

InputLabelProps

Props forwarded directly to the <FormLabel> element rendered above the trigger button. Use this to add a custom className, style, or any other label attribute without reaching into the DOM manually.

<SelectArrayInput
  source="tags"
  choices={choices}
  InputLabelProps={{ className: "text-primary font-semibold" }}
/>

createValue

Sentinel string used to identify the create option inside the options list. Defaults to "@@ra-create". Override when your real dataset happens to contain that string as a valid choice id.

<SelectArrayInput
  source="tags"
  choices={choices}
  onCreate={handleCreate}
  createValue="__new__"
/>

createLabel

Label for the create menu entry shown when no filter has been typed (i.e. the dropdown just opened). Defaults to the ra.action.create translation key (typically "Create"). Pair with createItemLabel to cover both the empty and non-empty filter states.

<SelectArrayInput
  source="tags"
  choices={choices}
  onCreate={handleCreate}
  createLabel="Add a new tag"
/>

createItemLabel

Label for the create menu entry that appears when the user has typed a filter string. Defaults to the ra.action.create_item translation key (typically "Create %{item}"). Use this to provide a custom label that includes context for your domain.

<SelectArrayInput
  source="tags"
  choices={choices}
  onCreate={handleCreate}
  createItemLabel='Add "%{item}" as a new tag'
/>

createHintValue

Internal sentinel string passed to ra-core's useChoices / useSupportCreateSuggestion to mark the "create" option while the create element is rendered. You rarely need to set this manually; override only when the default sentinel conflicts with a real option value in your dataset.

<SelectArrayInput
  source="tags"
  choices={choices}
  onCreate={handleCreate}
  createHintValue="@@create-tag"
/>

create

A React element rendered as a special entry at the bottom of the options list. When the user clicks it, ra-core's useSupportCreateSuggestion hook mounts the element (typically a dialog or inline form) and appends the newly-created record id to the selection.

Use create when you need full UI control over the creation form. For a simple callback, prefer onCreate.

import { useState } from "react";
 
const CreateTag = ({ onClose }) => {
  const [name, setName] = useState("");
  return (
    <div className="p-2">
      <input value={name} onChange={(e) => setName(e.target.value)} />
      <button onClick={() => onClose(name)}>Add</button>
    </div>
  );
};
 
<SelectArrayInput source="tags" choices={choices} create={<CreateTag />} />;

Alternatives

Consider the following alternatives for selecting multiple values from a list: