ARTICLE

Magento Customer Data Sections on Hyva: Private Content, Section Invalidation, and Why the Mini-Cart Shows Stale Data

Magento Customer Data Sections on Hyva: Private Content, Section Invalidation, and Why the Mini-Cart Shows Stale Data

A stale mini-cart on a Hyva storefront almost always means Magento’s private content was not refreshed after a state change. Hyva loads all customer section data as one object, re-fetches it only when the private_content_version cookie changes, the hour expires, or storage is cleared, and never reloads after an Ajax POST unless your code asks it to.

That one sentence explains most of the “the cart says 0 items but the checkout has 3” tickets that land after a Luma to Hyva migration. The rest of this article explains the mechanism in detail, maps the Luma model to the Hyva model, and ends with a debugging checklist you can hand to a developer or a QA lead.

What customer data sections are and why Magento needs them

Magento renders most storefront pages from the full page cache. A cached category page is the same HTML for every visitor, which is what makes it fast. But some parts of the page are personal: the mini-cart count, the customer’s first name in the header, the wishlist count, the compare list, recently viewed products, and messages such as “You added X to your cart.”

Magento calls these parts private content. Instead of rendering them on the server, it ships a generic page and fills in the private parts in the browser. Adobe’s private content documentation describes the pieces:

  • A section source is a PHP class that implements Magento\Customer\CustomerData\SectionSourceInterface. Its getSectionData() method returns an array, for example the cart summary.
  • Sections are registered in di.xml on SectionPoolInterface through the sectionSourceMap argument.
  • The browser requests section data from the customer/section/load endpoint and stores the result in localStorage.
  • An etc/frontend/sections.xml file declares which controller actions make which sections stale.

This design is what lets a Magento store serve cached pages and personal data from the same URL. It also means that whenever the browser holds old section data, the shopper sees old information, even though the server state is correct.

How Luma handles section data

On a Luma storefront the work is done by customer-data.js, a RequireJS module in Magento_Customer. Reading the source in the 2.4-develop branch, a few details matter for debugging:

  • Section data lives in localStorage under the mage-cache-storage key, with invalidation flags in mage-cache-storage-section-invalidation.
  • The mage-cache-sessid cookie is used to detect a new session, and the section_data_ids cookie tracks a data version per section.
  • The module listens to jQuery’s ajaxComplete. When a POST, PUT, or DELETE Ajax request finishes, it looks up the affected sections from the sections.xml map, invalidates them, and reloads them right away.
  • If the Ajax response JSON contains a redirect or backUrl, the immediate reload is skipped because the browser is about to change pages anyway.

The sections.xml rules from the Adobe documentation are worth memorizing:

  1. Magento invalidates private content on a POST or PUT request, not on GET.
  2. An action declared with no sections invalidates all of them, and <section name="*"/> reloads all of them.
  3. If another module declares sections for the same action, the documentation warns that this “will override the initial sections and only newly added sections will be invalidated.” A third-party module can quietly remove cart from the list for a core action.
  4. Using GET for a state change can let the full page cache store the response, which prevents the private content from updating.

Luma also gives developers fine control. A module can subscribe to a single section with customerData.get('cart').subscribe(...) and react only when the cart changes, or call customerData.reload(['cart'], true) to refresh one section.

How Hyva handles section data differently

Hyva removes RequireJS, Knockout, and jQuery from the storefront, so customer-data.js is gone. The Hyva section data documentation describes a smaller, simpler replacement with some deliberate trade-offs.

Loading. Hyva initializes section data in the footer of every page and loads it on window load. Visitors who do not have a private_content_version cookie (most first-time guests) get default values rendered into the page and no request is made at all. That is a real performance gain over Luma, which often calls customer/section/load on the first visit.

When Hyva re-fetches. Hyva requests fresh data from the server when localStorage has been cleared, when one hour has passed, or when the value of the private_content_version cookie changes. Otherwise it reads the cached copy from localStorage.

One object, not many sections. The Hyva docs are explicit: “In Hyvä it is not possible to subscribe to specific sections,” and “private content sections are not invalidated individually.” All sections arrive together in a single object.

Listening for data. Components receive the data through the private-content-loaded window event. In Alpine.js that is @private-content-loaded.window="receiveData($event.detail.data)", and in plain JavaScript it is a normal addEventListener. The event fires after every reload, whether the data came from the server or from localStorage, so the handler must be safe to run more than once.

Reloading. Hyva treats a normal form POST as an invalidation, and the fresh data arrives on the next page load. After an Ajax POST, nothing happens automatically. The Hyva JavaScript events reference states that, unlike Luma, Hyva does not invalidate or reload section data on its own, and your code must dispatch the reload-customer-section-data event.

Default values. Since Hyva 1.3.6, default values for sections are configured in etc/frontend/di.xml on Hyva\Theme\ViewModel\CustomerSectionData using the defaultSectionDataKeys argument. Any section not listed defaults to an empty array, which matters if a component reads a nested key before data arrives.

Luma vs Hyva section data at a glance

Behavior Luma (customer-data.js) Hyva
Client library RequireJS module with Knockout observables Small inline script plus window events
Storage localStorage mage-cache-storage localStorage
Granularity Per section, can subscribe to one One object, no per-section subscribe
Invalidation Per section via sections.xml Not per section; whole object refreshes
Reload after Ajax POST Automatic via jQuery ajaxComplete Manual: dispatch reload-customer-section-data
Reload after form POST Invalidated, reloaded on next page Invalidated, fresh data on next page load
First-visit guest request Often calls customer/section/load No request without private_content_version cookie
Time-based refresh section_data_lifetime (default 60 minutes) One hour
How components read data customerData.get('cart') private-content-loaded event

The trade is clear. Hyva sends fewer requests and runs far less JavaScript, which helps both load time and interaction latency. The cost is that developers have to be deliberate about when data reloads, because the framework no longer guesses for them.

The five most common causes of a stale mini-cart on Hyva

1. An Ajax add-to-cart that never dispatches the reload event

This is the top cause. A custom quick-view, a product carousel with an “Add” button, or a ported extension posts to the cart controller with fetch(). The server adds the item, but the page never fires reload-customer-section-data, so the header count stays the same until the next page load. The fix is one line after a successful response:

window.dispatchEvent(new CustomEvent('reload-customer-section-data'));

If the mini-cart should also open, dispatch toggle-cart afterwards, which is the Hyva event for opening the cart drawer.

2. The state change happened over the REST API

Magento clears the private_content_version cookie when a frontend controller or GraphQL request uses POST. According to the Hyva documentation, it does not do this for the REST API. If a custom component, a headless widget, or a third-party script changes the cart or the customer through REST, Hyva sees no cookie change and keeps serving the cached object for up to an hour.

The documented server-side fix is to inject Magento\Framework\App\PageCache\Version into the code path that changes state and call $version->process(). That updates the cookie, and Hyva re-fetches on the next load.

3. The change was made with GET

A “remove item” link, a coupon applied through a query string, or a custom “reorder” action implemented as a GET request breaks two rules at once. Magento does not invalidate private content on GET, and the full page cache may store the response. Adobe’s documentation warns about exactly this. State changes belong in POST requests. If you run Varnish, the full page cache and hole-punching guide covers how cached responses and private blocks interact.

4. A ported Luma module expects per-section subscriptions

Many third-party extensions were written against customerData.get('section').subscribe(). On Hyva there is no per-section observable. A compatibility module has to rewrite these components to listen to private-content-loaded and read the relevant key from the full data object. If the compatibility layer only renders the markup and skips this step, the component shows whatever it rendered at page load and never updates.

5. Someone deleted the wrong cookie

When a developer wants to force a refresh, the tempting move is to delete private_content_version. The Hyva docs warn against deleting private_content_version or cookieVersion. The documented way to force a reload from the browser is to expire mage-cache-sessid and then dispatch the reload event:

hyva.setCookie('mage-cache-sessid', '', -1, true);
window.dispatchEvent(new CustomEvent('reload-customer-section-data'));

A related cause: product changes in the cart

Adobe’s documentation notes that product changes such as name, price, or status reach the cart section only when the section lifetime expires or a cart update action runs. The lifetime is the “Customer Data Lifetime” setting under Stores, Configuration, Customers, Customer Configuration, Online Customers Options. Adobe’s customer configuration reference lists the default as 60 minutes. A price changed in the admin can therefore look wrong in a shopper’s mini-cart for a while, even though checkout totals are correct. That is expected behavior, not a bug.

Writing section-aware components the right way on Hyva

A few patterns keep private content predictable.

Handle the event idempotently. Because private-content-loaded fires after every reload, a handler that appends DOM nodes or starts timers will duplicate them. Store the data in Alpine state and let the template render from it.

Run once when you need to. For one-off work, such as personalizing a banner with the customer’s first name, Hyva documents a pattern using addEventListener('private-content-loaded', handler, { once: true }).

Guard nested reads. Before data arrives, a section may be an empty array or a default value. Write cart.summary_count || 0, not cart.summary_count.toString(). Use defaultSectionDataKeys to give your custom section a sensible default shape.

Dispatch the reload after every Ajax mutation. Treat it as part of the contract for any component that changes cart, wishlist, compare, or customer data. Code review should catch a fetch(..., { method: 'POST' }) that is not followed by the reload event.

Keep sections small. All sections travel in one response. A custom section that returns a full product collection or recently viewed history with images makes every reload heavier. Return only the fields the storefront renders.

Test the flow, not just the page. An end-to-end test that adds to cart and asserts the header count catches the most common regression. Our guide to testing Alpine.js storefront interactions with Playwright shows how to structure those tests for cart and checkout.

A debugging checklist for stale private content

Work through these in order. Most issues are found in the first four steps.

  1. Reproduce in a clean profile. Open a private window, add to cart, and confirm the problem exists without old localStorage.
  2. Watch the network tab. After the action, is there a request to customer/section/load? If not, the client never asked for fresh data.
  3. Check the request method. Was the state change a POST through a frontend controller, a GraphQL mutation, a REST call, or a GET? REST and GET are the usual suspects.
  4. Compare cookies before and after. Did private_content_version change? If it did not, Hyva has no reason to re-fetch.
  5. Inspect localStorage. Look at the stored section object and its contents. Is the cart count already wrong in storage, or is the component failing to render a correct value?
  6. Check the component. Does it listen for private-content-loaded on the window? Does it read the right key? Our walkthrough on debugging Alpine.js on Hyva covers scope and reactivity traps that look like data problems.
  7. Look for sections.xml overrides. If the store still runs any Luma pages, such as a Luma checkout during a phased migration, search all modules for sections.xml entries on the same action. An override can silently drop cart.
  8. Check the cache layer. Confirm the action URL is not cached by Varnish or a CDN, and that private blocks are not being served from a shared cache.

Where this fits in a migration plan

Private content is one of the areas where a Luma to Hyva migration changes behavior rather than just markup. Every extension that touches the cart, wishlist, compare list, or customer greeting needs a review of how it reads and refreshes section data. Building that review into scoping avoids a launch week spent chasing “the cart is wrong” reports.

Bemeir is a Brooklyn ecommerce agency, and our Hyva development services and Magento development services cover this kind of storefront engineering for Magento Open Source and Adobe Commerce. We also work across Shopify development, Shopware development, and BigCommerce development, each of which handles cart state in its own way. You can read more about Bemeir, see our technology partners, or start at the Bemeir home page.

FAQ

Why does my Hyva mini-cart show the wrong item count after adding a product?

The add-to-cart request most likely ran through Ajax and never dispatched the reload-customer-section-data event. Hyva does not reload section data automatically after an Ajax POST. Add the dispatch after a successful response, and the header count updates without a page reload.

Does Hyva use sections.xml?

Hyva runs on the same Magento backend, so sections.xml still exists and Magento still uses POST requests to signal that private content changed. The difference is on the client. Hyva does not invalidate sections individually; it refreshes the whole section data object when the private_content_version cookie changes, the hour expires, or storage is cleared.

How do I force Hyva to reload customer section data?

Expire the mage-cache-sessid cookie with hyva.setCookie('mage-cache-sessid', '', -1, true) and then dispatch the reload-customer-section-data window event. Do not delete private_content_version or cookieVersion, which the Hyva documentation warns against.

Why does a cart change made through the REST API not show up on the storefront?

Magento clears the private_content_version cookie for frontend and GraphQL POST requests but not for REST. Without a cookie change, Hyva keeps its cached data. Call process() on Magento\Framework\App\PageCache\Version in the code path that changes state so the cookie updates.

How long can customer section data stay cached?

On Hyva, data is re-fetched after one hour even without other triggers. On the Magento side, the Customer Data Lifetime setting defaults to 60 minutes and controls when product changes such as price or name reach the cart section if no cart update happens first.

Let us help you get started on a project with Magento Customer Data Sections on Hyva: Private Content, Section Invalidation, and Why the Mini-Cart Shows Stale Data and leverage our partnership to your fullest advantage. Fill out the contact form below to get started.

more articles about ecommerce

Read on the latest with Shopify, Magento, eCommerce topics and more.