Appearance
Modifiers & Options
Overview
Modifiers allow customers to customize menu items. They are organized into Modifier Groups (e.g., "Size", "Toppings") containing individual options (e.g., "Small", "Medium", "Large").
Key Purpose: Enable product customization with extras, sizes, or removals.
Purpose
This page lets you create and manage modifier groups and their individual options so customers can customize menu items with extras, sizes, or removals.
Key Concepts
- Modifier Group: A named collection of related options (e.g., "Size", "Toppings") with a pricing mode, channel visibility, and settings for Optional/Required, allow-multiple-selection, allow-same-modifier-more-than-once, and a maximum number selectable.
- Modifier (Option): An individual choice within a group with a customer-facing
name, an optionalkitchen_name, an absoluteprice(Money — not a +/- adjustment), and an optional image. - Pricing Mode: One of No charge (all modifiers free), Individual (each modifier has its own price, stored in a
modifier_pricesmap on the group), or Group (all modifiers share a single groupprice). A tax rate can be set for Individual and Group pricing. - Reordering: Modifier groups have an
orderfield used for display sequence; it is set to the current count of groups on creation. (This field is marked@deprecatedin the model.)
Actions
Create Modifier Group
Add a new modifier group with a name (and optional description and POS name), pricing mode, tax rate (if not "No charge"), channel visibility, settings, and a list of modifier options. Each option is created and synced, and the group is synced to Square if the integration is active.
Edit Modifier Group
Update the group's name, settings, pricing, visibility, or its list of modifier options. Existing modifiers are updated in place; new ones are created. A ReloadMenu event fires so ordering channels reflect changes immediately.
Delete Modifier Group
Remove a modifier group permanently. The deletion syncs to Square if integrated and triggers a menu reload event.
Reorder Modifier Groups
Change the display order of modifier groups by submitting a new ordering array. Each group's order field is updated accordingly.
Location
- Backoffice Route:
/menus/modifiers(Navigation: Menus → Modifier Groups) - Backend Controller:
app/Http/Controllers/Api/ModifierGroupController.php - Vue Component:
src/views/menus/modifiers/ModifierGroups.vue(mounted bysrc/pages/menus/modifiers/index.vue)
Concepts
Modifier Group
A collection of related options. Examples:
- "Size" (Small, Medium, Large)
- "Toppings" (Cheese, Bacon, Mushrooms)
- "Sauce" (Ketchup, Mayo, BBQ)
Modifier Option
An individual choice within a group. Examples:
- "Large" (€2.00)
- "Extra Cheese" (€1.50)
- "No Onions" (€0.00)
Modifier Group Fields
Group Name
| Property | Value |
|---|---|
| Field ID | details.name |
| Label | Modifier Group Name |
| Type | Text |
| Required | Yes |
| Validation | required, string |
Description: Name of the modifier group, used internally and shown to customers (kiosk, online ordering, etc.) unless a POS Name overrides it.
Examples:
- "Size"
- "Choose Your Toppings"
- "Select a Sauce"
Description
| Property | Value |
|---|---|
| Field ID | details.description |
| Label | Description |
| Type | Text |
| Required | No |
| Validation | nullable, string (UI enforces max 300 characters) |
Description: Optional descriptive text for the modifier group.
POS Name
| Property | Value |
|---|---|
| Field ID | pos_name |
| Label | POS Name |
| Type | Text |
| Required | No |
| Validation | nullable, string, unique per location |
Description: Optional internal name (e.g., a shorter label for the kitchen printer). Must be unique within the location. If left blank, the regular name is used everywhere.
Pricing
| Property | Value |
|---|---|
| Field ID | pricing |
| Label | Pricing |
| Type | Enum |
| Required | Yes |
| Allowed Values | no-charge, individual, group |
Description: How modifiers in the group are priced.
Business Logic:
- No charge (
no-charge): All modifiers are free (every modifier price is set to 0). - Individual (
individual): Each modifier has its own price (stored in the group'smodifier_pricesmap). - Group (
group): All modifiers share the single groupprice.
Group Price
| Property | Value |
|---|---|
| Field ID | price |
| Label | Price |
| Type | Currency (Money) |
| Required | Only when pricing is group |
| Validation | required, numeric (when pricing = group) |
Description: The single shared price applied to every modifier when Group pricing is selected.
Tax Rate Code
| Property | Value |
|---|---|
| Field ID | tax_rate_code |
| Label | Tax Rate |
| Type | String |
| Required | No |
| Validation | nullable, string (only accepted when pricing is not no-charge) |
Description: Tax rate applied to the group's modifiers. Not required, and not applicable for No charge pricing.
Visibility (Channels)
| Property | Value |
|---|---|
| Field ID | visibility |
| Label | Channels |
| Type | Array of channel enums |
| Required | Yes |
| Allowed Values | Kiosk, POS, Online Ordering, Table Qr Ordering, Uber Eats, Takeaway, Shopify |
Description: Sales channels on which the modifier group is visible. At least one channel must be selected.
Settings — Selection Type
| Property | Value |
|---|---|
| Field ID | settings.is_mandatory |
| Label | Selection Type (Optional / Required) |
| Type | Boolean |
| Required | Yes |
| Default | false (Optional) |
Description: When true (Required), the customer must pick a modifier from this group. When false (Optional), selection is not forced.
Settings — Allow Multiple Selection
| Property | Value |
|---|---|
| Field ID | settings.allow_select_more_than_one |
| Label | Allow customers to select more than one modifier |
| Type | Boolean |
| Required | Yes |
| Default | false |
Description: When true, customers can select multiple modifiers from the group, and a maximum-selectable dropdown appears.
Settings — Maximum Selected
| Property | Value |
|---|---|
| Field ID | settings.max_selected |
| Label | Maximum of modifiers a customer can select |
| Type | "No Maximum" or a number (2..N) |
| Required | Required only when allow_select_more_than_one is true |
Description: The maximum number of modifiers a customer can choose. Options are "No Maximum" or a specific number up to the count of modifiers in the group. Only shown/required when multiple selection is enabled.
Settings — Allow Same Modifier More Than Once
| Property | Value |
|---|---|
| Field ID | settings.allow_same_modifier_more_than_one |
| Label | Allow customers to select the same modifier more than once |
| Type | Boolean |
| Required | Yes |
| Default | false |
Description: When true, a customer can add the same modifier multiple times (e.g., double sugar, double sauce).
Order (deprecated)
| Property | Value |
|---|---|
| Field ID | order |
| Label | (internal) |
| Type | Number |
| Required | No (system-managed) |
Description: Display-ordering value, set to the current count of groups on creation. Marked @deprecated in the model; reordering still writes to it via the reorder endpoint.
Modifier Option Fields
Option Name
| Property | Value |
|---|---|
| Field ID | name |
| Label | Name |
| Type | Text |
| Required | Yes |
| Validation | required, string |
Description: Customer-facing name of the option.
Examples:
- "Small"
- "Extra Cheese"
- "No Pickles"
Kitchen Name
| Property | Value |
|---|---|
| Field ID | kitchen_name |
| Label | Kitchen Name |
| Type | Text |
| Required | No |
| Validation | nullable, string |
Description: Optional clearer/shorter label for kitchen and printer staff.
Price
| Property | Value |
|---|---|
| Field ID | price |
| Label | Price |
| Type | Currency (Money — absolute amount, not a +/- adjustment) |
| Required | Yes |
| Validation | required, numeric |
Description: The modifier's price as an absolute amount. With Group pricing every modifier is forced to the group price; with No charge every modifier is set to 0; with Individual pricing each modifier keeps its own value.
Examples:
- "Large": 2.00
- "Extra Cheese": 1.50
- "Ketchup": 0.00
Image
| Property | Value |
|---|---|
| Field ID | image (image.source, image.file) |
| Label | Image |
| Type | File upload |
| Required | No |
| Validation | image.source is a valid image source; image.file max 10240 KB |
Description: Optional image for the modifier option, stored as a content entity (its content_id is saved on the modifier record). Used where modifier images are shown (e.g., kiosk).
Business Logic
Price Calculation with Modifiers
Item Base Price: €10.00
Selected Modifiers (absolute prices):
- Size: Large (€2.00)
- Extra Cheese (€1.50)
- Bacon (€2.00)
- No Onions (€0.00)
Item Total: €10.00 + €2.00 + €1.50 + €2.00 + €0.00 = €15.50
Quantity: 2
Line Total: €15.50 × 2 = €31.00Pricing Modes
No charge → every modifier price is forced to 0
Individual → each modifier keeps its own price (stored in group's modifier_prices map)
Group → every modifier is forced to the single group priceValidation Flow
Customer selects item
│
▼
For each modifier group attached to item:
│
├── Is group Required (settings.is_mandatory)?
│ └── Yes → Must have a selection
│
├── Is multiple selection allowed (allow_select_more_than_one)?
│ └── Yes → Enforce settings.max_selected ("No Maximum" or a number)
│
└── Validate all rules pass
│
▼
Allow "Add to Cart"Customer Impact
Online Ordering Flow
- Customer clicks item
- Item detail modal opens
- Modifier groups displayed in order
- Required groups marked with badge
- Customer makes selections
- Price updates in real-time
- "Add to Cart" enabled when valid
Kiosk Flow
- Customer taps item
- Full-screen customization
- Step through each modifier group
- Large touch targets for options
- Running total displayed
- Confirm and add to order
KDS Display
Cheeseburger
- Size: Large
- Extra Cheese
- No Pickles
- Add BaconReceipt Format
Cheeseburger €8.95
Large €2.00
Extra Cheese €1.50
Bacon €2.00
--------
€14.45Relations
Depends On
- Nothing (standalone feature)
Affects
- Menu Items: Items reference modifier groups
- Order Total: Modifier prices affect total
- KDS: Modifiers shown for preparation
- Reports: Modifier popularity tracked
Related Features
Business Rules
- Pricing must be one of No charge (
no-charge), Individual (individual), or Group (group). With Individual pricing each modifier's price is stored separately in amodifier_pricesmap on the group; with Group pricing every modifier is forced to the single groupprice; with No charge every modifier price is 0. - A
tax_rate_codeis only accepted when pricing is not No charge, and it is validated only when provided. pos_nameis optional and must be unique within the location; if blank, the group's name is used everywhere.visibilityis required and must contain at least one channel from: Kiosk, POS, Online Ordering, Table Qr Ordering, Uber Eats, Takeaway, Shopify.- Modifier groups are synced to external integrations (Square) on create, update, and delete operations.
- Deleting a modifier group does not automatically remove it from items that reference it; items will simply no longer display the removed group.
- The
orderfield on a newly created modifier group is set to the count of existing groups, placing it last by default. The field is marked@deprecatedin the model. - Modifier option images are uploaded and stored as content entities; the
content_idis saved on the modifier record after media processing.
FAQs
What is the difference between modifiers and variants? Modifiers are add-on customizations (toppings, sauces) sharing the item's inventory. Variants are distinct product variations (sizes) with their own SKU and inventory tracking.
How is a modifier priced? Each modifier has an absolute
price(not a +/- adjustment). The price actually applied depends on the group's pricing mode: No charge forces every price to 0, Group forces every modifier to the single group price, and Individual lets each modifier keep its own price.Can I share one modifier group across multiple items? Yes. The same modifier group can be attached to many items; you attach it from each item's own editor.
"Where do I create and manage modifier groups?" Go to Menus → Modifier Groups (
/menus/modifiers). Click "Create Modifier Group" to add one, or use the row actions to Edit or Delete an existing group."What's the difference between the Name and the POS Name fields?" "Modifier Group Name" is the customer-facing/internal name and is required. "POS Name" is optional, used for internal purposes only (e.g. a shorter name on the kitchen printer), and must be unique within the location. If left blank, the regular name is used everywhere.
"What pricing options does a modifier group have?" Three: No charge (modifiers are free), Individual (each modifier has its own price), and Group (all modifiers share one price set on the group). A tax rate can be set for Individual and Group pricing; it isn't required for No charge.
"If I pick Group pricing, can each modifier have a different price?" No. With Group pricing every modifier is forced to the single group price, and with No charge every modifier is set to 0. Choose Individual pricing if each option needs its own price.
"How do I let customers choose more than one modifier from a group?" In Settings, turn on "Allow customers to select more than one modifier." A "Maximum of modifiers a customer can select?" dropdown then appears (No Maximum or a specific number). Leaving the toggle off makes it single-choice.
"How do I make a group mandatory?" In Settings, set the selection type to Required (the default is Optional). Required means the customer must pick a modifier from this group.
"What is 'Allow customers to select the same modifier more than once'?" A Settings toggle that lets a customer add the same option multiple times (e.g. double sugar, double sauce) rather than just once.
"What's the 'Kitchen Name' on a modifier for?" Each modifier has an optional Kitchen Name alongside its customer-facing Name — use it to give kitchen/printer staff a clearer or shorter label.
"Which sales channels can a modifier group appear on?" In the Channels section you pick visibility per channel: Kiosk, POS, Online Ordering, Table Qr Ordering, Uber Eats, Takeaway, and Shopify. At least one channel must be selected.
"I attached a modifier group but it doesn't show on my item — where do I attach it?" On the modifier group page the "Items" section is read-only and just lists items already using the group. You attach a group to a product from the item's own editor, not from the modifier group screen.
"Why are my MplusKassa/ShopCaisse/Lightspeed modifier group's fields greyed out?" Modifier groups synced from an external POS are partially or fully read-only. Name, settings, pricing, and prices come from the source system and would be overwritten on the next sync, so they're locked.
"Will my changes to a modifier group show up in online ordering right away?" Yes. Saving an edit fires a menu-reload so ordering channels pick up the change immediately, and create/update/delete are synced to active integrations such as Square.
"Can I add a photo to a modifier?" Yes. Each modifier option supports an image upload, used where modifier images are shown (e.g. kiosk).
Troubleshooting
Problem: Modifier group not showing on item
Causes:
- Group not attached to item
- Group has no modifiers
- Group's channel visibility excludes that channel
Solutions:
- Attach the group from the item's own editor
- Add modifiers to the group
- Check the group's Channels (visibility) selection
Problem: Customer can't add item to cart
Causes:
- A Required group (
settings.is_mandatory) has no selection - Validation error
Solutions:
- Check which group is set to Required
- Review the group's settings
Problem: Price not calculating correctly
Causes:
- Wrong pricing mode for the group
- A modifier's price is incorrect
Solutions:
- Confirm the pricing mode (No charge / Individual / Group) is what you intend
- Review each modifier's price (remember Group forces all to the group price, No charge forces all to 0)
Examples
The JSON below mirrors the create/update request payload (
details,pos_name,pricing,price,tax_rate_code,visibility,settings,modifiers[]).location_idis also required on the request but omitted here for brevity.
Size Selection (Required, single, individual pricing)
json
{
"details": { "name": "Size", "description": "Choose your size" },
"pos_name": "Size",
"pricing": "individual",
"tax_rate_code": "standard",
"visibility": ["Kiosk", "POS", "Online Ordering"],
"settings": {
"is_mandatory": true,
"allow_select_more_than_one": false,
"allow_same_modifier_more_than_one": false
},
"modifiers": [
{ "name": "Small", "kitchen_name": "S", "price": 0 },
{ "name": "Medium", "kitchen_name": "M", "price": 1.50 },
{ "name": "Large", "kitchen_name": "L", "price": 3.00 }
]
}Extra Toppings (Optional, multiple selection up to 3, individual pricing)
json
{
"details": { "name": "Extra Toppings" },
"pricing": "individual",
"tax_rate_code": "standard",
"visibility": ["Kiosk", "POS", "Online Ordering", "Takeaway"],
"settings": {
"is_mandatory": false,
"allow_select_more_than_one": true,
"max_selected": 3,
"allow_same_modifier_more_than_one": false
},
"modifiers": [
{ "name": "Extra Cheese", "kitchen_name": "Cheese", "price": 1.50 },
{ "name": "Bacon", "kitchen_name": "Bacon", "price": 2.00 },
{ "name": "Mushrooms", "kitchen_name": "Mushr", "price": 1.00 },
{ "name": "Jalapeños", "kitchen_name": "Jala", "price": 0.75 }
]
}Choose Your Sauce (Required, single, no charge)
json
{
"details": { "name": "Choose Your Sauce" },
"pricing": "no-charge",
"visibility": ["Kiosk", "POS", "Online Ordering"],
"settings": {
"is_mandatory": true,
"allow_select_more_than_one": false,
"allow_same_modifier_more_than_one": false
},
"modifiers": [
{ "name": "Ketchup", "price": 0 },
{ "name": "Mayonnaise", "price": 0 },
{ "name": "BBQ Sauce", "price": 0 },
{ "name": "Hot Sauce", "price": 0 },
{ "name": "No Sauce", "price": 0 }
]
}Add Extra Shot (Optional, group pricing — every modifier €0.80)
json
{
"details": { "name": "Add Extra Shot" },
"pricing": "group",
"price": 0.80,
"tax_rate_code": "standard",
"visibility": ["Kiosk", "POS"],
"settings": {
"is_mandatory": false,
"allow_select_more_than_one": true,
"max_selected": 2,
"allow_same_modifier_more_than_one": true
},
"modifiers": [
{ "name": "Espresso Shot", "kitchen_name": "Shot", "price": 0.80 },
{ "name": "Decaf Shot", "kitchen_name": "Decaf", "price": 0.80 }
]
}Remove Ingredients (Optional, multiple selection, no charge)
json
{
"details": { "name": "Remove Ingredients" },
"pricing": "no-charge",
"visibility": ["Kiosk", "POS", "Online Ordering"],
"settings": {
"is_mandatory": false,
"allow_select_more_than_one": true,
"max_selected": "No Maximum",
"allow_same_modifier_more_than_one": false
},
"modifiers": [
{ "name": "No Lettuce", "price": 0 },
{ "name": "No Tomato", "price": 0 },
{ "name": "No Onion", "price": 0 },
{ "name": "No Pickles", "price": 0 }
]
}