Overview
Product Addons lets you attach extra options to any WooCommerce product — from a simple "add gift wrapping?" toggle to a multi-step product configurator with live price calculation.
Everything is organised into addon groups. A group is a reusable bundle of fields with its own pricing, conditional logic and assignment rules. You build a group once, then assign it to as many products, categories or customer roles as you like. A single product can show several groups at once.
21 field types
Text, numbers, swatches, visual cards, product options, file uploads, date pickers and more — each priced however you like.
Dynamic pricing
Fixed, percentage, per-character, per-word, per-file or custom formula pricing — recalculated server-side.
Conditional logic
Show, hide, require or unrequire fields based on other selections, the cart, the date or the customer.
Live totals
Shoppers see the price update as they configure, with an optional itemised breakdown.
Requirements & installation
| Requirement | Minimum version |
|---|---|
| WordPress | 6.4 or newer |
| WooCommerce | 8.0 or newer |
| PHP | 8.0 or newer |
- Install the plugin. In WordPress go to Plugins → Add New → Upload Plugin, choose the
phox-product-addons.zipfile and click Install Now. - Activate it. A new Product Addons item appears in the main admin menu (look for the layout icon).
- Confirm WooCommerce is active. Product Addons extends WooCommerce, so WooCommerce must be installed and active first.
Any user who can Manage WooCommerce (shop managers and administrators) can create and edit addon groups. No extra capabilities are needed.
Update in place — replace the files inside the existing phox-product-addons plugin folder, or use the built-in updater. Never upload the new version as a second folder (for example phox-product-addons-latest) alongside the old one: WordPress would load both copies, and the duplicate PHOX_ADDONS_VERSION constant will cause a fatal error. Keep exactly one copy active.
Quick start — your first addon
Let's add an optional gift-wrapping fee to a product in under a minute.
- Open Product Addons from the admin menu, then click New Addon Group (or pick a ready-made template — see Starter templates).
- Name the group at the top, e.g. "Gift options".
- Add a field. On the Fields tab click Add field and choose Toggle Switch. Label it "Add gift wrapping" and set its price to a fixed
3.95. - Assign the group. On the Assignments tab add a target — choose Entire store, or search for specific products or categories.
- Set it live. On the Settings tab set Status to Active, then click Save.
- Check the product page. The toggle now appears below the product, and ticking it adds £3.95 to the live total and the cart.
The admin interface
The plugin opens on the groups list, where every addon group is shown with its field count, priority and status. From here you can create, duplicate, export, import or delete groups, and toggle the global itemised totals setting.
Opening a group reveals the group editor, organised into four tabs:
| Tab | What you configure |
|---|---|
| Fields | The options shoppers see — their type, labels, pricing, images and order. Drag to reorder. |
| Assignments | Which products the group appears on, and how it combines with other groups. |
| Logic | Rules that show, hide, require or make optional based on conditions, with a live test panel. |
| Settings | Status, priority, group type, display mode and the group description. |
The toolbar shows an "Unsaved changes" indicator whenever you have edits in flight. Nothing goes live until you press Save.
Field types
Product Addons ships with twenty-one field types across five categories. Add as many as you like to a group and drag them into any order.
Basic Basic
| Field | Description | Pricing |
|---|---|---|
| Text | A single-line text input. Supports a placeholder and maximum length. | Fixed, %, per-char, per-word, formula |
| Textarea | Multi-line text, ideal for messages or instructions. | Fixed, %, per-char, per-word, formula |
| Number | A numeric input. Can be given a formula variable name so its value feeds calculator formulas. | Fixed, %, formula |
| Email-validated text input. | Fixed, %, formula | |
| Telephone | Phone-number input. | Fixed, %, formula |
| URL | Web-address input. | Fixed, %, formula |
Selection Selection
| Field | Description |
|---|---|
| Checkbox | A single yes/no checkbox with an optional add-on price when ticked. |
| Checkbox Group | Multiple options where the shopper can tick several at once. Each option carries its own price. |
| Radio Buttons | Multiple options, one selectable. |
| Select Dropdown | A classic dropdown, one selectable. |
| Multi Select | A dropdown allowing several selections. |
| Toggle Switch | A modern on/off switch — perfect for "add this" style upsells. |
| Button Group | Options rendered as a row of selectable buttons, one selectable. |
Visual Visual
| Field | Description |
|---|---|
| Image Dropdown | A rich dropdown with a thumbnail, label, description and optional badge per option. |
| Visual Cards | Large selectable cards with image, title, description and badge. Choose 1–4 columns. Supports single or multiple selection. |
| Image Swatches | Compact image tiles with a zoom/preview lightbox. Single or multiple selection. |
| Colour Swatches | Colour chips (solid or two-tone). Single or multiple selection. |
Products Products
| Field | Description |
|---|---|
| Product Options | Offer other WooCommerce products (or specific variations) as selectable options — priced from each product's live price, stock-aware, and added to the order as their own line. See Product options. |
Advanced Advanced
| Field | Description |
|---|---|
| File Upload | Lets shoppers upload artwork or documents. Single or multiple files, with strict type and size validation. See File uploads. |
| Date Picker | A calendar date input with optional earliest/latest limits. |
| Time Picker | A time input with optional earliest/latest limits. |
| Date & Time | A combined date-and-time input with optional limits. |
Common field settings
Every field shares a few settings: a label, an optional description shown beneath it, and a Required toggle. Text-style fields add a placeholder and maximum length; option fields add an options editor; visual fields add image and badge controls.
Building addonsOptions, images & badges
Selection and visual fields are built from a list of options. For each option you can set:
- Label — what the shopper sees.
- Value — the stored identifier used in logic and on the order.
- Price & price type — a per-option add-on (fixed amount or percentage).
- Image or colour — for image dropdowns, visual cards and swatches (colour swatches accept a second colour for a two-tone chip).
- Badge — a small label such as "Popular" or "Bestseller" shown on cards and image dropdowns.
- Description — extra copy on cards and image dropdowns.
Images are chosen through the standard WordPress media library, so anything already in your media library is available instantly.
Building addonsPricing
Pricing is set per field (or per option). Every priced selection is added to the product price, multiplied by quantity, and shown in the live total and the cart. All prices are recalculated authoritatively on the server when the item is added to the cart, so totals can never be tampered with from the browser.
| Price type | Effect | Available on |
|---|---|---|
| None | No charge. | All fields |
| Fixed | A flat amount in your store currency. | All priced fields |
| Percentage | A percentage of the product price. | All priced fields |
| Per character | Amount × number of characters entered. | Text, Textarea |
| Per word | Amount × number of words entered. | Text, Textarea |
| Per file | Amount × number of files uploaded. | File Upload |
| Formula | A custom expression — see below. | All priced fields |
Addon prices are calculated per product unit. If a shopper buys a quantity of three, the addon price is applied to each of the three units, exactly like the base product price.
Formulas & calculator groups
Formula pricing turns Product Addons into a made-to-measure calculator. Set a field's price type to Formula and enter an expression such as:
(width * height) * 15
Formulas support +, -, *, / and parentheses. The available variables are:
product_price— the current product (or variation) price.quantity— the chosen quantity.- Any Number field's formula variable name. For example, set a Number field's variable name to
widthand another toheight, and both become usable in formulas.
Calculator groups
Set a group's type to Calculator and give the group itself a formula. The result is added as a single priced line labelled with the group title — ideal for area- or volume-based pricing where the charge comes from the whole configuration rather than one field.
Formulas are evaluated with a safe expression parser — never eval — and the result can never go below zero. The same engine runs in the browser (for the live total) and on the server (for the authoritative cart price), so the two always agree.
Conditional logic
The Logic tab lets fields react to what the shopper does. A rule reads as: when these conditions are met, perform these actions.
Actions
| Action | Effect |
|---|---|
| Show | Reveals a field. Fields targeted by any Show action start hidden and only appear when a rule passes. |
| Hide | Hides a field. |
| Require | Makes a field mandatory. |
| Make optional | Removes a field's required status. |
Conditions
Conditions can test much more than other fields. Each rule holds one or more condition groups; within a group you choose whether all or any conditions must pass, and you choose whether all or any groups must pass. This lets you build sophisticated AND/OR logic.
| Condition type | Tests… |
|---|---|
| Field value | Another field — equals, doesn't equal, contains, greater than, less than, is empty, is not empty. |
| Cart total | The cart subtotal — greater than, less than, equals. |
| Product in cart | Whether a given product is (or isn't) already in the cart. |
| Category in cart | Whether any product from a category is (or isn't) in the cart. |
| Month | The current month — great for seasonal options. |
| Day of week | The current day, Monday–Sunday. |
| Logged in | Whether the visitor is signed in. |
| User role | The visitor's role — e.g. wholesale customers. |
Rules can target and test fields in other groups assigned to the same product, so a choice in one group can drive a field in another. A built-in test panel lets you trial values and watch the resulting visibility and requirements before going live.
Assignments
The Assignments tab decides where a group appears. Add one or more targets; they are combined with OR, so the group shows whenever any target matches.
| Target | Matches |
|---|---|
| Entire store | Every product. |
| Specific products | Products you search for and select. |
| Product categories | All products in the chosen categories. |
| Product tags | All products with the chosen tags. |
| Product types | Simple, variable, grouped, external, subscription, bundle or composite. |
| User roles | Only shoppers with the chosen role. |
Combining groups
When several groups match the same product, the group's mode and priority decide how they interact:
| Mode | Behaviour |
|---|---|
| Merge | The group is shown alongside other matching groups (the default). |
| Override | The highest-priority matching group replaces all lower-priority ones. |
| Exclude | The group is never displayed — useful for temporarily switching one off. |
Priority is a number; higher-priority groups load first and win in Override mode.
Building addonsDisplay modes & layouts
A group's display mode (on the Settings tab) controls how its fields are laid out on the product page.
| Mode | Layout |
|---|---|
| Cards | Fields stacked as clean cards (the default). |
| Grid | Fields arranged in a responsive grid. |
| Accordion | Each field is a collapsible panel; the first is open by default. |
| Tabs | Fields become tabs, shown one at a time. |
| Steps | A wizard with Back/Next navigation and a final review panel summarising every choice. |
Tabs and Steps automatically skip fields that conditional logic has hidden, so shoppers never land on an empty step.
Modal popup New
Instead of showing a group's fields inline, you can tuck them behind a button that opens a popup. Tick Open options in a modal popup on the group's Settings tab — it wraps whichever display mode you've chosen (Cards, Grid, Tabs, Steps and so on), so it works over any layout.
On the product page the shopper then sees:
- A trigger button in place of the inline fields, with text you set via Modal button text (default "Customise Product").
- A popup containing all the group's fields plus a live total, opened by the button and dismissed with the close icon, the backdrop or the Esc key.
- A confirm button — text set via Confirm button text (default "Save options") — which validates the group's required fields before closing. If something's missing the popup stays open and highlights it.
- Once confirmed, the trigger turns into a summary: a "Customised" tick, the added price, and an Edit link that reopens the popup.
Modal is ideal when a product has lots of options and you want to keep the product page tidy — the shopper configures everything in one focused dialog, then returns to a clean summary.
File uploads
The File Upload field lets shoppers attach artwork, proofs or documents to their order. You can allow a single file or several, and cap the number of files.
Allowed files & limits
By default the field accepts JPG, JPEG, PNG, SVG, PDF, DOC and DOCX files up to 10 MB each. Both limits can be changed by a developer (see Hooks & filters).
Security
Uploads are validated defensively, so executable files such as .php cannot be uploaded or run:
- Every file is checked against an allow-list of types — and the file's real contents are inspected, not just its extension, so a renamed script is rejected.
- Files are stored with randomised names in a dedicated
uploads/phox-addons/folder. - SVGs are sanitised to strip scripts and event handlers.
- The upload folder is hardened with rules that block execution of
.php,.phtml,.cgiand similar files and disable directory listing.
The automatic hardening uses an .htaccess file, which Apache and LiteSpeed honour. On nginx, .htaccess is ignored — add an equivalent server-level rule to block PHP execution under uploads/phox-addons/ for full defence in depth.
Date & time
The Date, Time and Date & Time fields collect scheduling information — delivery dates, appointment slots, event times and so on. Each offers an optional Allowed range with an Earliest and Latest limit shown side by side.
When a range is set, a value outside it is rejected both in the browser (the shopper sees an error and can't add to cart) and again on the server, so an out-of-range date can never reach an order.
For the Date & Time field, the Earliest/Latest controls capture a full date and time, so you can restrict, say, bookings to between 09:00 and 17:00 on a given day.
Visual cards & swatches
Visual Cards, Image Swatches and Colour Swatches turn flat option lists into a polished, tactile picker. Each supports:
- Single or multiple selection. Tick Allow multiple selections on the field to let shoppers choose several options at once (they render as checkboxes); leave it off for a one-of-many picker (radio behaviour).
- Per-option pricing and badges, so a "Premium" card can carry both an upcharge and a label.
- For Visual Cards, a 1–4 column layout.
- For Image Swatches, a zoom lightbox so shoppers can preview the full image.
- Replace the product image — optionally swap the main product photo for the selected option's image (see below).
Replace the product image New
Image Dropdown, Visual Cards and Image Swatches each have a "Replace product image with selected option" checkbox in the field's settings. Turn it on and, when a shopper picks an option that has an image, the product page's main image swaps to that option's image; clearing the choice (or selecting an option without an image) restores the original.
It works with the standard WooCommerce product gallery and with the Phox Elements / Phox Oxygen Product Display element, both detected automatically — so shoppers see the exact variant they're configuring. The original image and its responsive srcset are restored intact when the selection is cleared.
This is perfect for colour or finish pickers: pair an Image Swatches field with product-lifestyle photos so the hero image updates as the shopper chooses. The toggle is per field, so enable it only where option images should stand in for the product photo.
Product options New
The Product Options field lets you offer other products as selectable options — bundles, cross-sells or an "add a matching item" upsell. Each option points at a real WooCommerce product (or a specific variation), so its price, name and image come straight from that product and stay in sync.
Configuring options
- Product — search for and pick the product each option represents.
- Discount — an optional percentage off that product's live price, just for this option.
- Quantity — how many of the product a single selection adds (defaults to one).
- Label & badge — override the displayed name and add a small label such as "Popular"; leave the label blank to use the product's own name.
- Single or multiple selection, rendered as cards with the product thumbnail (choose 1–4 columns).
Pricing & stock
Options are priced from the product's live, sale-aware price (with any per-option discount applied) and multiplied by the option quantity. Because the price is read from the product itself, a later price change or sale on the linked product is reflected automatically. Out-of-stock products are shown as disabled and can't be selected, and any option whose product no longer exists or can't be purchased is skipped everywhere.
A selected product option is added to the order as its own line item at the (optionally discounted) price, so stock, reporting and fulfilment treat it like any normal purchase. Variable, grouped and external products can't be used as options — pick a simple product or a specific variation.
Starter templates
From the groups list you can create a group from a ready-made template — a fast way to learn the plugin or stand up common patterns. Each is created as a draft so you can review and adjust before going live.
Gift Wrapping
A wrap toggle, wrapping-paper colour swatches and a gift-message textarea, wired with logic so the paper and message appear only when wrapping is enabled.
Engraving
Per-character engraving text with a font choice.
Extended Warranty
Tiered warranty options as a priced selection.
Personalisation
A personalisation bundle for monograms and custom text.
Import & export
Any group can be exported to a JSON file from the groups list, and imported on another site (or the same one). This makes it easy to move configurations between staging and production, or to keep a backup of a complex configurator.
Imported groups arrive as drafts so nothing goes live unreviewed. Product and category assignments are dropped by default, since those IDs rarely match across sites — re-assign the group after importing (or keep assignments deliberately if you're importing onto the same site).
Product Options reference products by ID. When you move a group to another site those IDs won't match, so re-pick the linked products after importing — the same way you re-assign the group.
Settings & totals
Per-group settings
| Setting | Purpose |
|---|---|
| Status | Active shows the group on matching products; Draft keeps it hidden while you work. |
| Priority | Ordering/precedence between groups — higher loads first and wins in Override mode. |
| Group type | Standard, Global, Template or Calculator (Calculator adds a group-level formula line). |
| Display mode | Cards, Grid, Accordion, Tabs or Steps. |
| Modal popup | Open the group's fields in a button-triggered popup instead of inline. Adds Modal button text and Confirm button text. See Modal popup. |
| Description | Optional text shown under the group title on the product page. |
Itemised totals
A global toggle on the groups list controls how add-ons appear in the totals box. With itemised totals on, each priced selection gets its own line; with it off, all add-ons are summed into a single "Options" row.
WorkflowThe storefront & cart
On the product page, add-ons appear below the product with a live totals box that updates as the shopper configures. Required fields are validated before the item can be added to the cart. If an image field has Replace product image enabled, the product's main photo also updates as the shopper chooses.
From there, selections flow through the whole order:
- The shopper's choices and their prices are shown under the item in the cart and at checkout.
- Add-on charges are added to the line item's price.
- Selections are saved to the order and appear in admin order details and in order emails.
- Product Options add the chosen product to the order as its own line item, at the discounted price where set.
- Uploaded files are linked from the order so you can download exactly what the customer sent.
Developer reference
Data model
Addon groups are stored as a hidden custom post type, phox_addon_group. The entire configuration — fields, options, assignments, logic and settings — is serialised as JSON in the _phox_config post meta.
| Where | Key |
|---|---|
| Group storage | Post type phox_addon_group, meta _phox_config |
| Front-end input names | phox_addons[{field_id}] (array-suffixed for multi-value fields) |
| Cart item data | phox_addons on the cart item |
| Order item meta | _phox_addons |
Pricing and logic are implemented twice — once in JavaScript for instant feedback and once in PHP for the cart. The PHP engine always recalculates prices from the raw submitted values, so the browser can never set its own price. Product Options resolve their price live from the referenced product, never from the submitted value.
REST API
The React admin is powered by a REST API under the phox-addons/v1 namespace. All group endpoints require the manage_woocommerce capability.
| Method | Route | Purpose |
|---|---|---|
GET | /groups | List all groups. |
POST | /groups | Create a group. |
GET | /groups/{id} | Fetch one group. |
PUT | /groups/{id} | Update a group. |
DELETE | /groups/{id} | Delete a group. |
POST | /groups/{id}/duplicate | Duplicate a group. |
GET | /groups/{id}/export | Export a group as JSON. |
POST | /groups/import | Import a group from an export payload. |
GET | /settings · POST /settings | Read / write global settings. |
GET | /search/products | Search products for assignments and Product Options. |
POST | /upload | Public shopper upload endpoint (nonce-protected). |
The upload route is public (shoppers may be guests) but protected by a nonce plus strict file validation. Any custom code calling it from a logged-in context must send the standard X-WP-Nonce header so the request keeps its user identity — otherwise the upload nonce will fail with a "Session expired" error.
Hooks & filters
Actions
// Render a product's addon groups inside a custom template / page builder.
// Fires the same renderer WooCommerce's product page uses.
do_action( 'phox_addons_product_display', $product );
Filters
// Change which groups apply to a product.
add_filter( 'phox_addons_groups_for_product', function ( $groups, $product ) {
return $groups;
}, 10, 2 );
// Change the maximum upload size (bytes). Default 10 MB.
add_filter( 'phox_addons_max_upload_size', fn() => 20 * MB_IN_BYTES );
// Change the allowed upload types (extension => mime).
add_filter( 'phox_addons_allowed_upload_types', function ( $types ) {
$types['ai'] = 'application/postscript';
return $types;
} );
Developer reference
Front-end JS API
Each rendered addon block exposes a small API on its root element, so custom add-to-cart implementations can validate and read the live total. The block also dispatches a bubbling phox-addons:change event whenever a selection changes.
const root = document.querySelector('[data-phox-addons]');
// Validate required fields before submitting. Returns true if valid.
if ( ! root.phoxAddons.validate() ) return;
root.phoxAddons.refresh(); // recompute visibility & totals
root.phoxAddons.total; // current per-unit addon total
root.phoxAddons.setProductPrice(p); // override price (variation-aware hosts)
// React to configuration changes.
root.addEventListener('phox-addons:change', (e) => {
console.log(e.detail.total);
});
Any custom add-to-cart UI must POST the phox_addons[...] inputs along with the request and should call root.phoxAddons.validate() first. The server re-runs validation and pricing regardless.
The image-swap runs automatically on the standard WooCommerce gallery and the Product Display element. If your custom gallery uses different markup, ensure the main image matches one of .woocommerce-product-gallery__image img or .ppd-main-img for the swap to find it.
Page-builder integration
Product Addons integrates with custom product templates — including the Phox Elements and Phox Oxygen "Product Display" elements — that don't use WooCommerce's default form.cart.
To render add-ons inside a custom template, fire the action where you want them to appear:
do_action( 'phox_addons_product_display', $product );
Then, in your add-to-cart handler:
- Call
root.phoxAddons.validate()and stop if it returnsfalse. - Append the serialised
phox_addons[...]fields to the POST body so they reach the cart. - For variation-aware layouts, call
setProductPrice()when the selected variation changes so percentage and formula pricing follow the live price.
The renderer guards against rendering twice for the same product on a page, so it's safe whether it fires via the standard WooCommerce hook or your custom template.
Help
Troubleshooting
Breakdance or the site shows "Constant PHOX_ADDONS_VERSION already defined"
Two copies of the plugin are active at once — usually the original phox-product-addons folder plus a second upload such as phox-product-addons-latest. WordPress loads both, and the second defines the same constant, which triggers a fatal error. Deactivate and remove one copy so only a single phox-product-addons folder remains, then update in place rather than adding a parallel folder.
The group doesn't appear on the product
Check that its Status is Active (not Draft), that it has at least one Assignment target matching the product, and that its mode isn't set to Exclude. If another matching group uses Override at a higher priority, it may be replacing this one.
A product option isn't showing
Product Options skip anything that can't be sold: a product that's out of stock appears but is disabled, and an option whose product has been trashed, set to draft, or changed to a variable/grouped/external type is dropped entirely. Point the option at a purchasable simple product or a specific variation.
The product image doesn't swap
Confirm the field has Replace product image with selected option ticked and that the chosen option actually has an image. The swap targets the standard WooCommerce gallery and the Phox Product Display element; a heavily customised or third-party gallery may use different markup (see Front-end JS API).
A file upload says "Session expired. Please reload the page"
This means the upload's security nonce didn't validate. It most often happens when a product page is served from a cache for a long time, or when a custom add-to-cart calls the upload endpoint without forwarding the logged-in session. Reload the page to refresh the nonce; for custom integrations, send the X-WP-Nonce header (see REST API).
The live total looks wrong
The cart price is always recalculated on the server, so the cart is the source of truth. If the browser total disagrees, it's usually a stale cached copy of the plugin's script — clear any page/CDN cache. Hidden fields (via logic) are never charged.
New features or styles aren't showing
Clear your site and browser caches. The plugin versions its assets automatically, but aggressive CDNs sometimes hold an old copy.
HelpFAQ
Can one product show several addon groups?
Yes. Every group whose assignment matches the product is shown (in Merge mode), and logic can even reference fields across those groups.
Are add-on prices charged per item?
Yes — add-on prices are per unit and are multiplied by the quantity, just like the base product price.
Can I offer other products as add-ons?
Yes — use the Product Options field. Each option links to a real product or variation, is priced from its live price (with an optional discount), respects stock, and is added to the order as its own line item. See Product options.
Can an option change the product image?
Yes. On image fields, enable Replace product image with selected option and the main product photo swaps to the chosen option's image, restoring when the choice is cleared. See Visual cards & swatches.
Can shoppers select more than one visual card or swatch?
Yes. Enable Allow multiple selections on the field and it behaves like a checkbox group while keeping the visual styling.
Is it safe to let customers upload files?
Yes. Uploads are restricted to an allow-list of document and image types, validated by real content rather than extension, stored with randomised names, and the folder is hardened against script execution. SVGs are sanitised. See File uploads.
Can I move a configuration between sites?
Yes — export the group to JSON and import it elsewhere. It arrives as a draft with assignments cleared, ready to review and re-assign. Re-pick any Product Options, since they reference products by ID.
Phox Product Addons Pro · v1.1.0 · by Phox Elements · Built for WooCommerce. For the latest information visit phoxelements.com.
