Modifier Picker
ProThe 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.00Choose a cooking point and a side
Installation#
Usage#
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.
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.
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.
Touch density#
Inside the touch density every option grows to 64px and the copy to 16px, with no props.
Grilled sirloin
€24.00Choose 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.
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.
| Prop | Type | Default | Description |
|---|---|---|---|
| value | Record<string, string[]> | - | The choices, controlled. A single choice group still holds an array. |
| defaultValue | Record<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. |
| basePrice | number | 0 | The item's own price. The total adds each chosen option's price to it. |
| formatPrice | (amount: number) => string | amount.toFixed(2) | Formats option prices and the total. |
| Data attribute | Description |
|---|---|
| data-slot | "modifier-picker". |
| data-valid | Present while every rule is met. |
ModifierGroup
One question about the item. Its rule decides how many options can and must be chosen.
| Prop | Type | Default | Description |
|---|---|---|---|
| name* | string | - | The key in value. |
| type | "single" | "multiple" | "multiple" | One option, or several. |
| required | boolean | false | At least one option must be chosen. Shorthand for min={1}. |
| min | number | - | The fewest options to choose, for multiple groups. |
| max | number | - | 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 attribute | Description |
|---|---|
| data-slot | "modifier-group". |
| data-invalid | Present 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 attribute | Description |
|---|---|
| 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.
| Prop | Type | Default | Description |
|---|---|---|---|
| children | ReactNode | - | Replaces the generated text. |
| Data attribute | Description |
|---|---|
| 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 attribute | Description |
|---|---|
| data-slot | "modifier-options". |
ModifierOption
One choice. Tap again to take it back, except the only choice of a required group.
| Prop | Type | Default | Description |
|---|---|---|---|
| value* | string | - | Stored in the group's array. |
| price | number | 0 | Added to the total while chosen, and shown as +€2.00. A negative price shows as a discount. |
| disabled | boolean | false | Takes the option out, for example when the kitchen runs out. |
| Data attribute | Description |
|---|---|
| data-slot | "modifier-option". |
| data-state | "on" or "off". |
| data-at-limit | Present 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 attribute | Description |
|---|---|
| data-slot | "modifier-option-media". |
ModifierOptionLabel
The option's name. Plain text children get one automatically, so you only write it beside ModifierOptionMedia.
| Data attribute | Description |
|---|---|
| data-slot | "modifier-option-label". |
ModifierPickerFooter / ModifierPickerTotal
The closing row, and the live total with tabular figures.
| Data attribute | Description |
|---|---|
| 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.
| Prop | Type | Default | Description |
|---|---|---|---|
| children | ReactNode | "Add to order" | The label. |
| Data attribute | Description |
|---|---|
| data-slot | "modifier-picker-submit". |
| data-incomplete | Present while a rule is unmet. |