Complete guide to the addCompositeToCart mutation for adding configured composite products to the WooCommerce cart.
The addCompositeToCart mutation adds a fully configured composite product to the cart.
| Field | Type | Required | Description |
|---|---|---|---|
productId | Int! | Yes | Composite product database ID |
quantity | Int | No | Composite quantity (default: 1) |
configuration | [CompositeProductConfigurationInput!]! | Yes | Component configurations |
extraData | String | No | JSON string of additional data |
| Field | Type | Required | Description |
|---|---|---|---|
componentId | ID! | Yes | Component ID from the composite |
productId | Int | No | Selected product ID (null for hidden) |
quantity | Int | No | Quantity for this component |
variationId | Int | No | Variation ID if product is variable |
variation | [ProductAttributeInput] | No | Attribute selection for variable products |
hidden | Boolean | No | Skip component (set quantity to 0) |
| Field | Type | Description |
|---|---|---|
cart | Cart | Updated cart object |
cartItem | CompositeCartItem | The added composite cart item |
Add a composite with all components configured:
mutation AddCompositeToCart( $productId: Int! $configuration: [CompositeProductConfigurationInput!]! ) { addCompositeToCart( input: { productId: $productId quantity: 1 configuration: $configuration } ) { cart { contents { itemCount } total } cartItem { key quantity total } } }
Variables:
{ "productId": 123, "configuration": [ { "componentId": "1", "productId": 456, "quantity": 1 }, { "componentId": "2", "productId": 789, "quantity": 1 } ] }
Important: All components must be included in the configuration, even optional ones.
For required components, provide a valid product selection:
{ "componentId": "1", "productId": 456, "quantity": 1 }
For optional components you want to skip, set quantity to 0:
{ "componentId": "2", "productId": 0, "quantity": 0 }
Or use the hidden flag:
{ "componentId": "2", "hidden": true }
When a component option is a variable product:
mutation AddCompositeWithVariation( $productId: Int! $configuration: [CompositeProductConfigurationInput!]! ) { addCompositeToCart( input: { productId: $productId quantity: 1 configuration: $configuration } ) { cart { total } cartItem { components { component { title } productId quantity } } } }
Variables:
{ "productId": 123, "configuration": [ { "componentId": "1", "productId": 456, "variationId": 457, "quantity": 1 } ] }
Alternatively, specify attributes to match a variation:
{ "productId": 123, "configuration": [ { "componentId": "1", "productId": 456, "quantity": 1, "variation": [ { "attributeName": "pa_color", "attributeValue": "blue" }, { "attributeName": "pa_size", "attributeValue": "large" } ] } ] }
Respect component min/max quantity constraints:
mutation AddCompositeWithQuantities( $productId: Int! $configuration: [CompositeProductConfigurationInput!]! ) { addCompositeToCart( input: { productId: $productId quantity: 1 configuration: $configuration } ) { cartItem { components { component { title minQuantity maxQuantity } quantity } } } }
Variables:
{ "productId": 123, "configuration": [ { "componentId": "1", "productId": 456, "quantity": 2 }, { "componentId": "2", "productId": 789, "quantity": 3 } ] }
The returned cart item includes composite-specific fields:
fragment CompositeCartItemFields on CompositeCartItem { key quantity total subtotal # Composite product product { node { name } } # Component data in cart components { productId quantity component { componentId title pricedIndividually discount } } }
| Field | Type | Description |
|---|---|---|
productId | Int | Selected product ID |
component | CompositeProductComponent | Component configuration |
quantity | Int | Quantity in cart |
Building a composite configuration form:
// Query composite data first const COMPOSITE_QUERY = gql` query GetComposite($id: ID!) { product(id: $id, idType: DATABASE_ID) { ... on CompositeProduct { databaseId name components { componentId title optional minQuantity maxQuantity defaultQuantity defaultOption { databaseId } queryOptions { nodes { databaseId name ... on VariableProduct { variations { nodes { databaseId name attributes { nodes { name value } } } } } } } } } } } `; // Build configuration from user selection function buildConfiguration(compositeData, userSelections) { return compositeData.components.map(component => { const selection = userSelections[component.componentId]; // Handle optional unselected components if (component.optional && !selection?.selected) { return { componentId: component.componentId.toString(), productId: 0, quantity: 0, }; } return { componentId: component.componentId.toString(), productId: selection?.productId ?? component.defaultOption?.databaseId, quantity: selection?.quantity ?? component.defaultQuantity, variationId: selection?.variationId, }; }); } // Add to cart mutation const ADD_COMPOSITE = gql` mutation AddComposite( $productId: Int! $configuration: [CompositeProductConfigurationInput!]! ) { addCompositeToCart( input: { productId: $productId quantity: 1 configuration: $configuration } ) { cart { total contents { itemCount } } cartItem { key total components { component { title } productId quantity } } } } `;
When scenarios affect component visibility, client code should:
function evaluateScenarios(scenarios, selections) { const hiddenComponents = new Set(); const hiddenOptions = new Map(); for (const scenario of scenarios) { if (!scenario.enabled) continue; // Check if scenario conditions match const matches = scenario.configuration.every(config => { const selected = selections[config.component.componentId]; const matchesOption = config.componentOptions.nodes .some(opt => opt.id === selected?.productId); switch (config.optionsModifier) { case 'IS': return matchesOption; case 'IS_NOT': return !matchesOption; case 'IS_ANY': return selected?.productId != null; default: return false; } }); if (matches) { // Apply scenario actions for (const action of scenario.actions) { if (!action.isActive) continue; action.hiddenComponents?.forEach(c => hiddenComponents.add(c.componentId) ); // Handle hidden options... } } } return { hiddenComponents, hiddenOptions }; }
Common errors:
| Error | Cause |
|---|---|
Composite product not found | Invalid product ID |
Invalid component | componentId doesn't belong to the composite |
Missing required component | Required component not in configuration |
Invalid product selection | Product not available for component |
Quantity out of range | Quantity exceeds min/max limits |
Invalid variation | Variation doesn't match product |
Component out of stock | Selected product not in stock |
If the composite is configured with editableInCart: true, use updateItemQuantities with modified configuration:
mutation UpdateCompositeInCart( $key: ID! $configuration: [CompositeProductConfigurationInput!]! ) { updateItemQuantities( input: { items: [ { key: $key quantity: 1 configuration: $configuration } ] } ) { cart { total } } }
Creates a new composite product with components and scenarios. See the Mutations Reference for full input/output details.
mutation CreateComposite($input: CreateCompositeProductInput!) { createCompositeProduct(input: $input) { product { databaseId ... on CompositeProduct { layout components { componentId title optional minQuantity maxQuantity } scenarios { name enabled } } } } }
Variables:
{ "input": { "name": "Custom PC Build", "regularPrice": 999.00, "layout": "PROGRESSIVE", "components": [ { "title": "Processor", "queryType": "SELECT_PRODUCTS", "queryIds": [201, 202, 203], "minQuantity": 1, "maxQuantity": 1, "pricedIndividually": true }, { "title": "Graphics Card", "queryType": "SELECT_PRODUCTS", "queryIds": [301, 302], "optional": true, "pricedIndividually": true, "discount": "5" } ], "scenarios": [ { "name": "Default Configuration", "actions": [ { "actionId": "compat_group", "isActive": true } ] } ] } }
Updates an existing composite product. When updating components or scenarios, provide the id field to update existing entries, or omit it to create new ones. Set delete: true with an id to remove entries.
mutation UpdateComposite($input: UpdateCompositeProductInput!) { updateCompositeProduct(input: $input) { product { databaseId ... on CompositeProduct { components { componentId title } } } } }
Variables (update existing component):
{ "input": { "id": 500, "components": [ { "id": "12345", "title": "Updated Processor", "queryIds": [201, 202, 203, 204] } ] } }
Composite product mutations also accept addons and excludeGlobalAddons fields. See Per-Product Addon Fields.