Modifier Picker

Pro

The choices an item needs before it goes in the order, shown as pictures: cooking point, side, paid extras and what to leave out, with the rules in words and a live total.

Grilled sirloin

€24.00
Cooking pointRequired · Choose 1
SideRequired · Choose 1
ExtrasOptional · Up to 2
WithoutOptional

Choose a cooking point and a side

Installation#

# One-time setup (tokens + lib helpers)npx koalaui-cli@latest init# Activate your Pro license (once per machine)npx koalaui-cli@latest login <key># Add this component (its dependencies come along)npx koalaui-cli@latest add modifier-picker

Usage#

This example is part of Koala UI Pro

The preview is yours to explore. Its code comes with a Pro license, paid once and used in every project.

Have a license? Unlock it here

Open it in a Sheet or a Dialog from a Menu Grid tile. Read the choices and the total from onSubmit, or call useModifierPicker() inside it for a footer of your own.

Anatomy#

import {  ModifierGroup,  ModifierGroupHeader,  ModifierGroupRule,  ModifierGroupTitle,  ModifierOption,  ModifierOptionLabel,  ModifierOptionMedia,  ModifierOptions,  ModifierPicker,  ModifierPickerFooter,  ModifierPickerSubmit,  ModifierPickerTotal,} from "@/components/ui/modifier-picker"<ModifierPicker>  <ModifierGroup>    <ModifierGroupHeader>      <ModifierGroupTitle />      <ModifierGroupRule />    </ModifierGroupHeader>    <ModifierOptions>      <ModifierOption>        <ModifierOptionMedia />        <ModifierOptionLabel />      </ModifierOption>    </ModifierOptions>  </ModifierGroup>  <ModifierPickerFooter>    <ModifierPickerTotal />    <ModifierPickerSubmit />  </ModifierPickerFooter></ModifierPicker>

Pictures, illustrations and icons#

A cook reads a cut of steak faster than the word “medium”. Put an illustration, a photo or an icon in ModifierOptionMedia and the option becomes a card, with its price as a tag on the picture. Groups without media stay compact, so a list of removals can sit under a row of pictures.

ExtrasOptional · Up to 2
WithoutOptional

Rules#

A type="single" group takes one option. A multiple group takes any number between min and max, and refuses a tap past the limit with a shake instead of greying options out. Leave out required and a single choice can be tapped off again. The rule line writes itself.

MilkOptional · Choose 1
FillingsRequired · Choose 2 to 3

Leaving things out#

variant="remove" is for what the guest does not want. A chosen option gets a cross and is struck through, so the kitchen reads it as a removal at a glance.

WithoutOptional

Touch density#

Inside the touch density every option grows to 64px and the copy to 16px, with no props.

Grilled sirloin

€24.00
Cooking pointRequired · Choose 1
SideRequired · Choose 1
ExtrasOptional · Up to 2
WithoutOptional

Choose a cooking point and a side

Translated labels#

The rule line and the submit label come from LabelsProvider, with Spanish included. Pass children to either to write your own.

TortillaObligatorio · Elige 1
ExtrasOpcional · Hasta 2
7,00 €

API reference#

ModifierPicker

The root. Holds every group's choices in one object keyed by group name, adds up the total and checks the rules. Accepts className, merged last.

PropTypeDefaultDescription
valueRecord<string, string[]>-The choices, controlled. A single choice group still holds an array.
defaultValueRecord<string, string[]>-The choices when uncontrolled.
onValueChange(value) => void-Called on every change.
onSubmit(value, total: number) => void-Called by ModifierPickerSubmit once every rule is met. The total covers one item: multiply by your own quantity.
basePricenumber0The item's own price. The total adds each chosen option's price to it.
formatPrice(amount: number) => stringamount.toFixed(2)Formats option prices and the total.
Data attributeDescription
data-slot"modifier-picker".
data-validPresent while every rule is met.

ModifierGroup

One question about the item. Its rule decides how many options can and must be chosen.

PropTypeDefaultDescription
name*string-The key in value.
type"single" | "multiple""multiple"One option, or several.
requiredbooleanfalseAt least one option must be chosen. Shorthand for min={1}.
minnumber-The fewest options to choose, for multiple groups.
maxnumber-The most options to choose. A tap past it is refused: the rule line darkens and the header shakes. Ignored on single groups.
variant"add" | "remove""add"remove marks a chosen option with a cross and strikes it, for what to leave out.
Data attributeDescription
data-slot"modifier-group".
data-invalidPresent after a submit while the group's rule is not met.

ModifierGroupHeader / ModifierGroupTitle

The title row. The title names the options for screen readers, and the row shakes when a submit finds the group incomplete.

Data attributeDescription
data-slot"modifier-group-header" or "modifier-group-title".

ModifierGroupRule

Says the rule in words, from the group's props: "Required · Choose 1", "Optional · Up to 2". Turns destructive while the group is invalid.

PropTypeDefaultDescription
childrenReactNode-Replaces the generated text.
Data attributeDescription
data-slot"modifier-group-rule".

ModifierOptions

The options, as a Radix toggle group: arrow keys move between them, and a single choice group is announced as radios.

Data attributeDescription
data-slot"modifier-options".

ModifierOption

One choice. Tap again to take it back, except the only choice of a required group.

PropTypeDefaultDescription
value*string-Stored in the group's array.
pricenumber0Added to the total while chosen, and shown as +€2.00. A negative price shows as a discount.
disabledbooleanfalseTakes the option out, for example when the kitchen runs out.
Data attributeDescription
data-slot"modifier-option".
data-state"on" or "off".
data-at-limitPresent on unchosen options while the group is at its max.

ModifierOptionMedia

A 4:3 frame for a photo, an illustration or an icon. With it the option becomes a card: the media on top, the control and label below, and the price as a tag on the image. In a remove group, chosen media fades to grey.

Data attributeDescription
data-slot"modifier-option-media".

ModifierOptionLabel

The option's name. Plain text children get one automatically, so you only write it beside ModifierOptionMedia.

Data attributeDescription
data-slot"modifier-option-label".

ModifierPickerFooter / ModifierPickerTotal

The closing row, and the live total with tabular figures.

Data attributeDescription
data-slot"modifier-picker-footer" or "modifier-picker-total".

ModifierPickerSubmit

A Button that never disables. While a rule is unmet it marks the groups, shakes them and moves focus to the first one, so staff see what is missing instead of a grey button.

PropTypeDefaultDescription
childrenReactNode"Add to order"The label.
Data attributeDescription
data-slot"modifier-picker-submit".
data-incompletePresent while a rule is unmet.

FAQ#

A grey button does not say what is missing. Tapping it shows the incomplete groups, shakes them and moves focus to the first, which is faster with a queue at the bar.