How does the ajax Fetch API work in Dawn? Issues with FormData & adding multiple products

Topic summary

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.
  • Supports product properties: items[1][properties][prop key]
  • 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.

Hey everyone,

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.

Any advice? Thanks!

You have a create a JSON of all the products and then send it with fetch /add API.

You can contact me if you need help in setting up the upsell products.

Any luck with this? looks like multiple items cannot be sent with FormData :confused:

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:

console.log(Object.fromEntries(formData.entries()))
---
{
    "items[0][id]": "123456789",
    "items[0][quantity]": "1",
    "items[1][id]": "987654321",
    "items[1][quantity]": "1",
    "items[1][properties][first prop key]": "First prop value",
    "items[1][properties][second prop key]": "Second prop value"
}​
1 Like