4.06

Customer Data (Private Content)

Describe front-end usage of customer data: client-side private content, customerData API, sections.xml, and invalidation to keep FPC enabled.

Why This Matters: Customer Data (a.k.a. Private Content) enables full page caching while still personalizing user-specific UI (cart, welcome message, minicart) using localStorage + AJAX instead of server-side sessions in templates.

Customer Data (Private Content)

mindmap root((Customer Data)) Goal Keep FPC enabled Personalize via JS Storage Browser localStorage JS API Magento_Customer/js/customer-data get / reload / subscribe Sections etc/sections.xml Invalidation by actions /customer/section/load Use Cases Minicart Welcome message Messages

The Problem With Sessions in Templates

Rendering session-dependent content in *.phtml prevents Full Page Cache (FPC) and Varnish from caching the page HTML.

<!-- Avoid this pattern in templates -->
<div class="greeting">
  Welcome <?= $block->getSession()->getUsername(); ?>!
</div>

Instead, move user-specific pieces to the client side using the Customer Data framework.

Approach: Private Content on the Client

  • Local storage: User-specific data is persisted in the browser (localStorage) and hydrated via AJAX.
  • Placeholders: Server returns fully cached HTML with placeholders; JS fills them after load.
  • customerData JS module: Provides API to read, subscribe, and reload sections.

Using the customerData Module

Magento_Customer/js/customer-data
define(['Magento_Customer/js/customer-data'], function (customerData) {
    'use strict';

    // Read a section (Knockout observable)
    var cart = customerData.get('cart');

    // Get current value
    var cartData = cart();

    // React to changes
    cart.subscribe(function (newCart) {
        // update UI with newCart
    });

    // Force reload of one or more sections
    customerData.reload(['cart', 'customer'], true);
});
  • get(name) returns a KO observable; call it as a function to read, or subscribe to updates.
  • reload(sections, forceNew) triggers AJAX to /customer/section/load to refresh data.

Canonical Example

See Magento/Checkout/view/frontend/web/js/view/minicart.js for a canonical usage of customer data (minicart content and count).

Sections & Invalidation

The sections mechanism ensures that client-side data stays fresh when backend state changes.

  1. Configuration: Each module declares sections and invalidating actions in etc/sections.xml.
  2. Frontend loader: When data is missing/expired, Magento sends AJAX to fetch sections' JSON.
  3. Backend provider: Aggregates data for each section and returns JSON for the frontend.
  4. XHR Listener: JS listens for POST/PUT requests; matching actions invalidate affected sections.

sections.xml Example

app/code/Vendor/Module/etc/sections.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Customer:etc/sections.xsd">
    <section name="cart">
        <action name="checkout_cart_add"/>
        <action name="checkout_cart_updatePost"/>
        <action name="checkout_cart_delete"/>
    </section>
    <section name="customer">
        <action name="customer_account_loginpost"/>
        <action name="customer_account_logoutsuccess"/>
    </section>
</config>

When these actions occur (typically via POST), Magento invalidates the specified sections; the next page view or XHR triggers reload.

Rendering Private Content in HTML

Use data-mage-init or x-magento-init to bootstrap a small module that reads from customerData and updates the DOM.

<div class="greeting" data-role="greeting"></div>
<script type="text/x-magento-init">
{
  "[data-role=greeting]": {
    "Vendor_Module/js/greeting": {}
  }
}
</script>
view/frontend/web/js/greeting.js
define(['Magento_Customer/js/customer-data'], function (customerData) {
    'use strict';
    return function (element) {
        var customer = customerData.get('customer');
        var name = (customer() && customer().fullname) || 'Guest';
        element.textContent = 'Welcome ' + name + '!';
        customer.subscribe(function (data) {
            element.textContent = 'Welcome ' + ((data && data.fullname) || 'Guest') + '!';
        });
    };
});

Key Endpoints & Storage

  • Load endpoint: /customer/section/load (returns JSON for requested sections)
  • Storage: Section data is persisted in localStorage and synced with Knockout observables.
  • Expiry: Magento uses TTL to expire sections; expired/missing sections are reloaded automatically.

Best Practices

  • Avoid session-dependent templates: Prefer client rendering via customerData.
  • Use sections.xml: Declare invalidation for every backend action that affects private content.
  • Minimize payload: Keep section JSON small; render on the client efficiently.
  • Subscribe instead of polling: Use observables to react to updates.
  • Force reload when needed: customerData.reload(['section'], true) after custom AJAX updates.

Further Reading

Exam Tips

  • Goal: Keep FPC on; render personalized bits via JS from localStorage.
  • API: customerData.get('section') (observable), .subscribe(), .reload().
  • Invalidation: etc/sections.xml maps backend actions to sections; POST/PUT XHR triggers invalidation.
  • Endpoint: /customer/section/load provides sections JSON.
  • Example: Minicart, welcome message, messages area.