Theme Log Vol. 1 – Customization Round-Up (2025-05-04)

Welcome to Theme Log!

Theme Log is a series where we share weekly theme customizations, patterns, and cool Liquid tricks.

The plan is to post a new topic each week around Midnight on Sunday (ET) and have us all share various things we did with themes and the online store during that week. A sort of community showcase of having had the opportunity to make a positive impact on merchants and their customers!

:card_file_box: Got a trick? Did you build something cool?

If you have done something cool with any Online Store 2.0 theme, reply below with a snippet or a screenshot. Bonus points for sharing reusable code blocks!


If you are sharing something please try to adhere to this format:

# Add multiple products to cart on form submission [Short Name or Goal]

---
## :straight_ruler: Objective:
Briefly describe what you were trying to do (e.g., Add a trial upsell product with the purchase of another product).

---
## :clipboard: Affected Files:
List any theme files affected (e.g., buy-buttons.liquid, theme.js)

Modified:
- `snippets/buy-buttons.liquid`

Added:
- `snippets/trial-product-upsell.liquid`

---
## :light_bulb: Snippet/Explanation:
Use a code block for Liquid/JS/CSS and a short paragraph on how it works.

---

You can include multiple products in one product form and generally have them all add on submit (if native or `FormData` via Cart AJAX API).

[details="Code Snippet"]
```liquid
<input
  type="hidden"
  name="items[][id]"
  value="{{ product.selected_or_first_available_variant.id }}"
>
<input
  type="hidden"
  name="items[][quantity]"
  value="1"
>
{% if product.requires_selling_plan %}
<input
  type="hidden"
  name="items[][selling_plan]"
  value="{{ product.selected_or_first_available_variant.selected_or_first_available_selling_plan_allocation.selling_plan.id }}"
>
{% endif %}```
[/details]

---
## :test_tube: Notes/Tips:
Any edge cases, mobile quirks, or performance tips.

---
## :camera_with_flash: Screenshots / Before & After:
Use image embeds to showcase your positive impact visually. Be sure to include them in a `details` to keep things organized!

[details="Preview Experience"]
[/details]

Otherwise feel free to reply to others and connect further about whatever they share!

3 Likes

Add multiple products to cart on form submission


:straight_ruler: Objective:

The client wanted to be able to add a trial upsell product with the purchase of another product.


:clipboard: Affected Files:

Modified:

  • snippets/buy-buttons.liquid
  • assets/product-info.js
  • assets/product-form.js
  • config/settings_schema.json
  • layout/theme.liquid

Added:

  • snippets/trial-product-upsell.liquid

:light_bulb: Snippet/Explanation:

You can include multiple products in one product form and generally have them all add on submit (if native or FormData via Cart AJAX API).

Note, if you are including selling plans or properties for only specific products I believe you may need to include them as blank value inputs for each of the products listed. Also, if you do not want certain products included you can simply disable their associated inputs.

Code Snippet
<input
  type="hidden"
  name="items[][id]"
  value="{{ product.selected_or_first_available_variant.id }}"
>
<input
  type="hidden"
  name="items[][quantity]"
  value="1"
>
{% if product.requires_selling_plan %}
<input
  type="hidden"
  name="items[][selling_plan]"
  value="{{ product.selected_or_first_available_variant.selected_or_first_available_selling_plan_allocation.selling_plan.id }}"
>
{% endif %}

:test_tube: Notes/Tips:

  • With the theme I was working in, I needed to adjust the add to cart functionality to work with a third-party app-based cart drawer.
  • The Shop Pay accelerated checkout button also did not seem to like the use of name="items[][id]" instead of name="id" so I also recreated it.
  • I also worked on a solution to ensure the trial upsell product was not in the cart if the primary product was removed.
  • A few years back I would use this approach as a non-JS solution and then of course the Cart AJAX API when necessary as well.
  • Back then I also noticed that trying to submit the same form submission with multiple variants of the same product would provide unexpected results, such as increased quantities you did not submit.
    • I am currently unsure if that ever got fixed but I did contact Support about it at the time.

:camera_with_flash: Screenshots / Before & After:

Preview Experience


1 Like

Great initiative @RobDukarski! Love it.

I have a funny one, not particular advanced, but a nifty trick I occasionally use. So sorry for already deviating from the format.

Generally save serverside-accessible data to be used inbetween pages


:straight_ruler: Objective:

Usescases could be to

  • conditionally render a banner based off specific URL parameters
  • render different menus based off a user pick on index page (imagine Men vs Women).
  • save states like grid view (2 cols, 3cols, list view) or light/dark theme (example below)

:light_bulb: Snippet/Explanation:

/* On a button click/event/page load with URL param */
fetch(theme.routes.cartUpdate, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    attributes: { view: 'list' } // 'list' as example
  })
})
.catch((error) => console.error('Error updating collection view:', error.message));
{%- comment -%} Accessing the data with liquid {%- endcomment -%}
{%- assign default_view = cart.attributes.view | default: section.settings.default_view -%}

:test_tube: Notes/Tips:

The example I’ve used is for a button that changes the layout of collection pages. They can switch between grid and list view. I want the picked option to be remembered while browsing, without having to wait for javascript to update it.

On change (setting a new view) we need a page reload, or a Section Rendering API call to update the view.

I think the usual way to handle it, is sessionStorage. Then it’ll have to run and change on every load.


:camera_with_flash: Screenshots / Before & After:

Preview Experience


/cart.json


Thanks, things have just been busy over here for months and I have been wanting to break back into the community involvement, I figured putting together some series like this would be a great way to start!

Also, I love to use cart attributes all of the time for updating things throughout the storefront, like they are even perfect for “password-protected” content!

We’re in the same boat, then.

Yeah, it has plenty usecases.

Love this new series @RobDukarski - this will be super helpful for any dev working on themes!

1 Like