- Structure
- Template Metafields
- Liquid Section
- Add to Cart Properties
- FoxSell JS Object
- Storefront API
- FoxSell TS Types
Mix and Match Settings Metaobject
Only present in case
Hide fields
Hide fields
ID of the bundle within the app
List of unique product references selected within the app
Category Properties selected within the app
Show fields
Show fields
Title of the Category
Maximum amount of products to be selected from this category.
Only to be used when
Quantity as an option is unchecked.Only present in case
Quantity as an option is checked in the app. Show fields
Show fields
Title of the Option
Integer Variant ID of Quantity Option
Quantity of Products to be selected for this option
Add-on products available for this option. When no option-specific products are configured, FoxSell uses the bundle-level
add_on_products.List of product references selected as add ons within the app
Object is a Key Value Pair
{key: value}To be used in case
Quantity as an option is uncheckedShow fields
Show fields
Deprecated: Use
options_v2 instead. This field will be removed in a future version.List of objects with Key Value pair.
[{key: value}]Quantity as an option is checked in the app. Show fields
Show fields
Bundle Variant GID
Example:
gid://shopify/ProductVariant/XXXXXXXOption Properties
Show fields
Show fields
Title of the Option
Quantity of Products to be selected for this option
Deprecated: Use
categories instead. This field will be removed in a future version.Object is a Key Value Pair
{key: value}FoxSell stores the main Mix and Match bundle configuration in the
dynamic_add_ons_bundle metafield. Every Mix and Match template reads that base metafield, and templates can also store additional template-specific JSON metafields on the same bundle product in the same app namespace.Template metafields are in addition to
dynamic_add_ons_bundle, not replacements for it.- Base Metafield
- Step Template
- Glow Template
Required for every Mix and Match template. This metafield stores the shared bundle structure documented in the Structure tab, including categories, products, options, add-ons, pricing settings, and the bundle ID.
{% liquid
assign app_namespace = 'app--67872686081'
assign dynamic_add_ons_bundle_config = product.metafields[app_namespace].dynamic_add_ons_bundle.value
%}
Additional settings used by the Step template.
Hide fields
Hide fields
Step display and quantity rules. Items are matched to
dynamic_add_ons_bundle.categories by array index.Show fields
Show fields
Step title shown in the template.
Supporting text shown for the step.
Minimum number of products the shopper must select in this step.
0 makes the step optional.Maximum number of products the shopper can select in this step.
10000 is used for unlimited maximum quantity.Controls whether add-ons are picked automatically or shown as a separate step.
Show fields
Show fields
Add-on flow strategy. Values:
add_on_step or automatic_add.Add-on step title. Used when
strategy is add_on_step.Add-on step supporting text. Used when
strategy is add_on_step.Present when
strategy is automatic_add. Add-ons are saved with one variant per product.{% liquid
assign app_namespace = 'app--67872686081'
assign step_template_config = product.metafields[app_namespace].step.value
%}
{
"categories_metadata": [
{
"title": "Step 1",
"description": "Choose your cleanser",
"min_quantity": 1,
"max_quantity": 1
}
],
"add_on_settings": {
"strategy": "add_on_step",
"title": "Add-ons",
"description": "Complete your routine"
}
}
Additional settings used by the Glow template.
Hide fields
Hide fields
Controls add-on flow and whether add-on products are shared or configured per option.
{% liquid
assign app_namespace = 'app--67872686081'
assign glow_template_config = product.metafields[app_namespace].glow.value
%}
{
"quantity_rules": {
"strategy": "fixed",
"max": "cap_at_highest"
},
"add_on_settings": {
"strategy": "add_on_step",
"product_scope": "shared"
}
}
Add this snippet as a section in your theme to see the values of the metafieldNote: The
namespace and key are constant for all the stores{% liquid
assign app_namespace = 'app--67872686081'
assign dynamic_add_ons_bundle_config = product.metafields[app_namespace].dynamic_add_ons_bundle.value
assign bundle_id = dynamic_add_ons_bundle_config.bundle_id
assign options = dynamic_add_ons_bundle_config.options_v2.value
assign products = dynamic_add_ons_bundle_config.products.value
assign add_on_products = dynamic_add_ons_bundle_config.add_on_products.value
assign add_on_product_properties = dynamic_add_ons_bundle_config.add_on_product_properties.value
assign categories = dynamic_add_ons_bundle_config.categories.value
assign settings = dynamic_add_ons_bundle_config.settings.value
%}
<p><strong>Bundle ID</strong>: {{ bundle_id }}</p>
<p><strong>Options:</strong></p>
{% for option in options %}
{{ option | json }}
{% endfor %}
<p><strong>Categories</strong></p>
{% for category in categories %}
<p><strong>Title:</strong> {{ category.title }}</p>
<p><strong>Category Max Quantity (Only used if Options are Empty):</strong> {{ category.quantity }}</p>
<p><strong>Products</strong></p>
{% for p in category.products %}
{% liquid
assign int_product_id = p.id | times: 1
assign product_ref = null
paginate products by 128
for ref in products
if ref.id == int_product_id
assign product_ref = ref
break
endif
endfor
endpaginate
assign product_variants = p.variants
%}
<p><strong>Product ID:</strong> {{ p.id }}</p>
<p><strong>Product Title:</strong> {{ product_ref.title }}</p>
{% for variant_data in product_variants %}
{% liquid
assign variant_id = variant_data[0] | times: 1
assign variant_price = variant_data[1]
assign variant = product_ref.variants | where: 'id', variant_id | first
%}
{% if variant %}
<p><strong>Variant ID:</strong> {{ variant_id }}</p>
<p><strong>Variant Overridden Price:</strong> {{ variant_price }}</p>
<p><strong>Variant Ref:</strong> {{ variant | json }}</p>
{% endif %}
{% endfor %}
{% endfor %}
{% endfor %}
<p><strong> Add On Products</strong></p>
{% for p in add_on_products %}
{% assign product_gid = 'gid://shopify/Product/' | append: p.id %}
<p><strong>Title:</strong> {{ p.title }}</p>
<p><strong>Properties:</strong> (Format: {variant_id: price})</p>
<p>{{ add_on_product_properties[product_gid] | json }}</p>
{% endfor %}
<p><strong>Settings</strong></p>
{{ settings | json }}
{% schema %}
{
"name": "FoxSell M&M Add Ons Test",
"settings": [],
"presets": [
{
"name": "FoxSell M&M Add Ons Test"
}
]
}
{% endschema %}
Below is a simplified example that uses the
fetch API to add a bundle to the cart. The id is the variant ID of the bundle variant and quantity is the amount of the variant that you want to add to the cart.A unique bundle identifier is generated using the bundle ID (assumed to be stored in a variable called bundleId) and the current date. This identifier is passed as a line item property, __foxsell:dynamic_add_on_bundle_id. The bundled items that need to be added are passed in the property __foxsell:dynamic_add_on_bundle_items.const currentDate = Date.now()
const identifier = `${bundleId}_${currentDate}`
let formData = {
items: [
{
id: 51594067837240, // current bundle variant id
quantity: 1,
properties: {
"__foxsell:dynamic_add_on_bundle_id": identifier,
"__foxsell:dynamic_add_on_bundle_items": [
{
"variantId": 51594057843001,
"quantity": 1,
"category": "category1", // empty for 'addOns
"type": "product", // 'product' or 'addOns'
"properties": { "_location": "warehouse1" } // optional metadata per selection
},
{
"variantId": 51594057843000,
"quantity": 1,
"type": "addOns", // 'product' or 'addOns'
"properties": { "_location": "warehouse2" } // optional metadata per selection
}
... // more items
]
}
}
]
}
fetch(window.Shopify.routes.root + 'cart/add.js', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(formData)
})
.then(response => {
return response.json();
})
.catch((error) => {
console.error('Error:', error);
});
Add this snippet in your
liquid file to access bundle config via window.foxsell objectNote: The namespace and key are constant for all the stores{% liquid
assign app_namespace = 'app--67872686081'
assign dynamic_add_ons_bundle_config = product.metafields[app_namespace].dynamic_add_ons_bundle.value
assign bundle_id = dynamic_add_ons_bundle_config.bundle_id
assign options = dynamic_add_ons_bundle_config.options_v2.value
assign products = dynamic_add_ons_bundle_config.products.value
assign add_on_products = dynamic_add_ons_bundle_config.add_on_products.value
assign add_on_product_properties = dynamic_add_ons_bundle_config.add_on_product_properties.value
assign categories = dynamic_add_ons_bundle_config.categories.value
assign settings = dynamic_add_ons_bundle_config.settings.value
%}
<script>
window.foxsell = window.foxsell || {};
window.foxsell.bundleConfig = {
id: "{{ bundle_id }}",
options: {{ options | json }},
categories: {{ categories | json }},
settings: {{ settings | json }},
products: {{ products | json }},
addOnProducts: {{ add_on_products | json }},
addOnProductProperties: {
{% for product_property in add_on_product_properties %}
{% assign property_value = product_property[1] %}
'{{ product_property[0] | replace: "gid://shopify/Product/", "" }}': {
variants: {
{% for variant in property_value['variants'] %}
'{{ variant[0] | replace: "gid://shopify/ProductVariant/", ""}}': {{ variant[1] }},
{% endfor %}
}
},
{% endfor %}
}
}
</script>
Use the Shopify Storefront API to query the metafields namespace containing your bundle configuration and any template-specific metafields.Note: The
namespace and key are constant for all the stores- Base Config
- Step Template
- Glow Template
- Add to Cart
Query
dynamic_add_ons_bundle for every Mix and Match template.fragment Product on Product {
id
title
handle
dynamic_add_ons_bundle: metafield(
namespace: "app--67872686081"
key: "dynamic_add_ons_bundle"
) {
id
namespace
key
value
type
reference {
... on Metaobject {
id
handle
type
fields {
key
type
value
references(first: 10) {
nodes {
... on Product {
id
handle
title
}
}
}
}
}
}
}
}
Query
step along with dynamic_add_ons_bundle for Step template bundles. The step.value field contains a JSON string.fragment StepTemplateProduct on Product {
id
title
handle
dynamic_add_ons_bundle: metafield(
namespace: "app--67872686081"
key: "dynamic_add_ons_bundle"
) {
id
namespace
key
value
type
reference {
... on Metaobject {
id
handle
type
fields {
key
type
value
}
}
}
}
step: metafield(
namespace: "app--67872686081"
key: "step"
) {
id
namespace
key
value
type
}
}
Query
glow along with dynamic_add_ons_bundle for Glow template bundles. The glow.value field contains a JSON string.fragment GlowTemplateProduct on Product {
id
title
handle
dynamic_add_ons_bundle: metafield(
namespace: "app--67872686081"
key: "dynamic_add_ons_bundle"
) {
id
namespace
key
value
type
reference {
... on Metaobject {
id
handle
type
fields {
key
type
value
}
}
}
}
glow: metafield(
namespace: "app--67872686081"
key: "glow"
) {
id
namespace
key
value
type
}
}
Use Shopify’s Storefront API
cartCreate mutation to add a Mix and Match Bundle to the cart.Bundle items are passed as cart line attributes.mutation {
cartCreate(input: {
lines: [
{
merchandiseId: "gid://shopify/ProductVariant/XXXXXXX"
quantity: 1
attributes: [
{
key: "__foxsell:dynamic_add_on_bundle_id"
value: "BUNDLE_ID"
}
{
key: "__foxsell:dynamic_add_on_bundle_items"
value: "[{\"variantId\":VARIANT_ID_1,\"category\":\"Category1\",\"quantity\":1,\"type\":\"product\"},{\"variantId\":VARIANT_ID_2,\"category\":\"Category2\",\"quantity\":1,\"type\":\"product\",\"properties\":{\"Engraving\":\"Hello World\"}}]"
}
]
}
]
}) {
cart {
id
checkoutUrl
lines(first: 10) {
edges {
node {
id
quantity
merchandise {
... on ProductVariant {
id
title
}
}
attributes {
key
value
}
}
}
}
}
userErrors {
field
message
}
}
}
The Shopify Product Variant GID of the bundle variant being added to the cart.
Example:
gid://shopify/ProductVariant/48292426973435Cart Line Attributes
The bundle ID from FoxSell. This can be found in the
bundle_id field of the dynamic_add_ons_bundle_config metaobject.A JSON-encoded array of the selected bundle items. Each item in the array contains:
Hide fields
Hide fields
The integer Variant ID of the selected product
The category title this item belongs to (e.g.
"Top", "Bottom"). Use an empty string for add-on items.The quantity of this item in the bundle
Either
"product" for regular bundle items or "addOns" for add-on itemsOptional custom line item properties for the bundle component. Use this to pass custom data (e.g. engravings, personalizations) that will appear as line item properties on the order.
Example:
{"Engraving": "Hello World"}interface ProductProperties {
_location?: string;
[key: string]: any;
}
interface SelectedProduct {
variantId: string;
quantity: number;
category?: string;
type: 'product' | 'addOns';
properties?: ProductProperties;
}
interface PriceConfig {
strategy: 'fixed_pricing' | 'dynamic_pricing';
value: number;
}
interface AddOnConfig {
minimum: number;
maximum: number;
}
interface OptionV2 {
title: string;
variantId: string;
quantity: number;
addOn: AddOnConfig;
additionalInfo: any[];
price: PriceConfig;
}
interface CategoryProduct {
id: string;
variants: Record<string, number>;
}
interface Category {
title: string;
quantity: number;
products: CategoryProduct[];
}
interface AddOnProductProperties {
variants: Record<string, number>;
}
interface Settings {
addOn: AddOnConfig;
price: PriceConfig;
}
interface Config {
bundleId: string;
products: string[];
categories: Category[];
optionsV2: OptionV2[];
addOnProducts: string[];
addOnProductProperties: Record<string, AddOnProductProperties>;
settings: Settings;
}
Thanks to Sid from Numbered