← All posts Why Horizon Has No main-cart-items.liquid (and How to Customize the cart-items-component Instead)

Why Horizon Has No main-cart-items.liquid (and How to Customize the cart-items-component Instead)

Horizon replaced main-cart-items.liquid with a web component. Here is exactly where to customize cart items safely without breaking theme updates.

Horizon does not have a main-cart-items.liquid section file. That file exists in Dawn, not Horizon. In Horizon, cart item rendering is handled by main-cart.liquid (the section) together with the cart-items-component custom web element, and customizing the cart means working within that web-component architecture rather than editing a flat Liquid section.

Key takeaways

  • Horizon replaced the monolithic main-cart-items.liquid pattern with a self-contained cart-items-component web component.
  • Editing core Horizon files directly will be overwritten on the next theme update (Horizon ships weekly releases).
  • Cart attributes go in main-cart-footer.liquid; line-item properties are rendered via cart.items iteration inside main-cart.liquid.
  • The safe customization path is: new block files in blocks/, prefixed section files, and a GitHub upstream branch to pull Shopify's updates cleanly.
  • Apps built for older Online Store 2.0 architecture often break on Horizon's cart because web components run inside Shadow DOM.

Why the File You're Looking For Doesn't Exist

Shopify's help docs and countless tutorials reference main-cart-items.liquid because that is how Dawn structures its cart. Dawn splits the cart page into two sections: main-cart-items.liquid (the line-item table) and main-cart-footer.liquid (totals, checkout button, attributes). Horizon does not replicate this split.

Horizon is the flagship of a new generation of Shopify themes built on the Horizon framework. The architecture principle is: every meaningful interactive piece of the page is a self-contained web component. The cart drawer, the variant picker, the predictive search, none of them are monolithic Liquid sections the way Dawn implements them. Instead, they are encapsulated custom elements composed together inside a single main-cart.liquid section file.

This caused immediate confusion when Horizon launched at Summer Editions 2025. By February 2026, the Shopify Community forums were full of merchants on Horizon 3.3.0 searching their sections/ directory for main-cart-items.liquid and finding nothing, because it was never there.

What the Horizon Cart Architecture Actually Looks Like

Instead of a separate section file per cart zone, Horizon's cart page is structured around main-cart.liquid as the outer section, with the <cart-items-component> custom element handling the interactive line-item rendering inside it.

The key architectural decisions, straight from the official Horizon GitHub repo:

  • Server-rendered first. HTML is rendered by Shopify's servers via Liquid. Business logic like money formatting and translations stays on the server, not the client.
  • Web-native progressive enhancement. The theme targets modern browsers without polyfills, and async re-rendering of cart state (quantity updates, removals) is handled sparingly as a progressive enhancement on top of the server-rendered HTML.
  • Scoped JavaScript. Horizon includes a specific JS class that measures performance for components like the product form, cart drawer, and cart discount, keeping JS weight minimal per component.

The cart-items-component is the custom element (<cart-items-component>) that wraps the line-item table. It listens for cart change events (add, remove, quantity update) and re-renders its inner HTML via the Cart Ajax API without a full page reload. Its Liquid template lives inside main-cart.liquid, not in a separate file.

Cart attributes (order-level custom fields like a gift message or a date picker) are captured via main-cart-footer.liquid. Per Shopify's cart template docs, you add an <input> with name="attributes[your-field-name]" and form="cart" inside that footer file. That attribute is then accessible via {{ cart.attributes['your-field-name'] }}.

Line item properties (per-product custom data) are not rendered by cart-items-component automatically. You need to loop cart.items inside main-cart.liquid and output each item's .properties hash. Shopify's own docs note that if two of the same item are added with unique line item properties, they split into separate line items, a behaviour worth accounting for in your template logic.

How to Customize the Cart Safely on Horizon

Horizon releases weekly theme updates. If you edit core files directly, those edits will be overwritten the next time you apply an update. The GitHub repo even flagged a live version discrepancy: as of April 2026, the Theme Store was at v3.5.1 while the GitHub repo was pinned at v3.4.0, meaning some changes were landing in the store first. This reinforces the need for a proper Git workflow.

The three-branch GitHub setup

Shopify's official Growth Services guide (published November 2025) recommends managing all Horizon customizations through a disciplined branching strategy:

  1. upstream/horizon, tracks the official Shopify/horizon repository directly. You pull updates here first.
  2. main, your customized, production-ready branch. You merge upstream into main after reviewing diffs.
  3. Feature branches, all new cart customizations are built here and PR'd into main.

To wire up the upstream, navigate to your local theme folder and run:

git remote add upstream https://github.com/Shopify/horizon.git
git fetch upstream

When Shopify ships a new Horizon version, pull it into your upstream branch, resolve conflicts on your custom files, and merge. This keeps your cart customizations intact across every weekly update.

Where to actually put your cart customizations

What you want to changeWhere to put it
Order-level custom field (date picker, gift note)main-cart-footer.liquid via attributes[field-name] input
Line item property displayInside the cart.items loop in main-cart.liquid
New cart UI block (upsell, trust badge)New file in blocks/ with a unique prefixed name
Custom cart CSS{% style %} tags inside your block or section, not a separate CSS file
Overriding a core cart sectionDuplicate as sections/custom.main-cart.liquid, reference it in cart.json

Avoid the temptation to copy the full main-cart.liquid into a custom. prefixed version unless you absolutely must. A full copy means you have to manually diff and re-apply every future Shopify update to that section. Instead, add only what you need in new block files where possible, and keep the core section untouched.

Building interactive cart extensions as web components

Horizon's performance philosophy is explicit: no external carousel or UI libraries. If you need interactive behaviour inside the cart (an animated quantity stepper, a real-time free-shipping progress bar), build it as a vanilla Web Component that initialises only when visible. Use IntersectionObserver to defer initialisation on anything not immediately in the viewport, and write your styles inside {% style %} tags to keep them scoped and free of extra HTTP requests.

class CartUpsellBadge extends HTMLElement {
  connectedCallback() {
    // Initialise only when cart is open / element is visible
    this._observer = new IntersectionObserver((entries) => {
      if (entries[0].isIntersecting) {
        this._init();
        this._observer.disconnect();
      }
    });
    this._observer.observe(this);
  }

  _init() {
    // Fetch upsell data, render into this.innerHTML
  }
}
customElements.define('cart-upsell-badge', CartUpsellBadge);

Load this script via <script type="module"> so the browser handles it efficiently, matching Horizon's own script loading pattern.

The Shadow DOM App-Compatibility Problem

This is the gotcha that catches most merchants mid-project. Apps built for Dawn's older Online Store 2.0 architecture often hook into the cart by querying the DOM for a quantity input or a cart total span and listening for change events. That pattern breaks on Horizon.

Because Horizon's cart components are web components, apps using Shadow DOM encapsulation block the old document.querySelector('.cart-item__quantity') pattern. The custom element's internals are not accessible from outside the shadow root via standard DOM queries.

Before installing any cart app on Horizon, check the app listing explicitly for a "Horizon support" note. Apps that received a "Built for Shopify" badge have been performance-tested, but badge status alone does not guarantee Shadow DOM compatibility. When in doubt, install on a duplicate theme and test quantity updates, removals, and the checkout flow end-to-end before going live.

The Current GitHub Lag and What It Means for You

As of mid-2026, the Shopify/horizon GitHub repo has had periods where it lags behind the version live in the Theme Store. In April 2026, community developers noted the repo was at v3.4.0 while the Theme Store served v3.5.1. Shopify confirmed the main branch may include code for features not yet released, meaning the published theme and the repo can be out of sync in both directions.

Practical implication: do not treat the GitHub repo as the canonical source of the version running in your store. Pull the latest theme from your Shopify admin (Online Store > Themes > Download theme file) and diff it against the GitHub repo before making assumptions about what code is live. Use Theme Check (bundled into Shopify CLI and available as a VS Code extension that Horizon automatically recommends) to validate your Liquid before every deploy.

A Note on the Instagram In-App Browser Bug (June 2026)

A live issue worth knowing about: as of the week of June 14, 2026, multiple Horizon merchants reported that further navigation after the initial page load inside Instagram's, Facebook's, and TikTok's in-app browsers produces a blank white screen. Shopify's theme team acknowledged the issue on June 17, 2026, confirmed a workaround exists, and said the root cause may be related to in-app browser behaviour rather than the theme itself. If you are running social commerce campaigns that drive traffic through these in-app browsers, contact Shopify Support for the current workaround while the full fix is in progress.

Summary: The Correct Mental Model for Horizon's Cart

Stop looking for main-cart-items.liquid. It is a Dawn concept. Horizon's cart is:

  • One outer section: main-cart.liquid
  • One interactive web component: <cart-items-component> handling Ajax re-renders
  • One footer section: main-cart-footer.liquid for cart attributes and the checkout form
  • Blocks directory: where all your custom additions live

Work within that model, manage your changes through a proper GitHub upstream branch, and you can safely customize Horizon's cart without dreading every weekly update.

If you need hands-on help building out a custom cart experience on Horizon, see what we do as a Shopify theme developer or check our Shopify speed optimization service if cart performance is the underlying concern.

shopify horizonshopify theme developmentcart customizationliquidshopify developer

Frequently asked questions

Where is main-cart-items.liquid in Shopify Horizon?

It does not exist. Horizon replaced the separate main-cart-items.liquid section with a cart-items-component web component rendered inside main-cart.liquid. To customize cart item display, edit the cart.items loop inside main-cart.liquid or add new block files in the blocks/ directory.

How do I add custom cart fields (like a date picker or gift message) to Shopify Horizon?

Add a standard HTML input with the attribute name='attributes[your-field-name]' and form='cart' inside main-cart-footer.liquid. That value is then accessible anywhere in Liquid via {{ cart.attributes['your-field-name'] }}. Do not place order-level attributes inside main-cart.liquid or a blocks file.

Why do cart apps break on the Horizon theme?

Horizon renders its cart components as web components, and some use Shadow DOM encapsulation, which blocks the standard DOM queries that older apps rely on to find quantity inputs or cart totals. Check each app listing for explicit Horizon support before installing, and always test on a duplicate theme first.