A developer is attempting to add multiple products to cart with one click in Shopify’s Dawn theme but encountering issues with the Fetch API and FormData implementation.
Core Problem:
Receiving a 400 error with message “items must be a collection of items”
Unclear whether JSON.stringify is needed with FormData API
Payload appears incorrect in Chrome inspector
Attempted Solutions:
Tried passing items array directly in product-form.js
Attempted using hidden inputs in form markup
Initial approach used FormData.append with JSON.stringify, which failed
Working Solutions Identified:
JSON Approach (confirmed working):
Change fetchConfig() to use JSON format instead of FormData
Critical: Use variant IDs, not product IDs, or will get 404 error
Set up appropriate request body
FormData Approach (detailed solution):
Use a custom buildFormData() function to properly structure the data
Format items as nested array notation: items[0][id], items[0][quantity], etc.
Can also submit via native form with proper input naming convention
Key Takeaway: Multiple items cannot be sent directly with standard FormData.append() - requires either JSON format or a specialized buildFormData helper function to create proper nested structure.
Summarized with AI on November 15.
AI used: claude-sonnet-4-5-20250929.
I’m trying to add an upsell section on the product page in Dawn - one click should add 3 products. The liquid part is easy, but I’m stuck on the ajax.
I’ve seen a few tutorials on using Fetch like this one and was able to replicate that method, but it seems this isn’t the method used in Dawn, and I wanted to understand how Dawn does it, and have a more seamless integration.
For the Add to Cart button, Dawn uses the FormData API and passes the product ID with a hidden input. Why can’t I do this with multiple items also? E.g. -
Another thing I tried is to pass the items list in product-form.js, under the line where formData is defined.
let items = [
{id:41643147788484,quantity:1},
{id:41643131044036,quantity:1},
{id:41572158046404,quantity:1}
]
const formData = new FormData(this.form);
formData.append('items', JSON.stringify(items))
Neither of these work, I’m getting a 400 error and the error message is “must be a collection of items: items”
Do I even need to use stringify in FormData API? My payload in chrome inspector seems ok.
Couldn’t get it to work with FormData. Doesn’t look like items works for it.
Did get it to work with json though.
Had to change the fetchConfig() to json and setup the appropriate body. Something important is to make sure you’re using variant.ids and not product.ids or you’ll get a 404 error on the request.
If you want to go through a native form submit, your html would look like this:
Notice how the second product also includes 2 product properties
Quantity default to 1, so you might just ommit it sometimes
If you want to go through javascript and submit your data dynamically (with formData API), your code would look like this:
const items = [
{
"id": "123456789",
"quantity": 1
},
{
"id": "987654321",
"quantity": 1,
"properties": {
"first prop key": "First prop value",
"second prop key": "Second prop value"
}
},
]
const formData = new FormData();
buildFormData(formData, 'items', items);
function buildFormData(formData, key, data) {
if (data === Object(data) || Array.isArray(data)) {
for (const i in data) buildFormData(formData, `${key}[${i}]`, data[i]);
} else data && formData.append(key, data);
}
buildFormData function is self explanatory. It should output something like this when you log your formData Object, which you can then use in your body request: