PerformanceMay 18, 20269 min read

Wishlist Apps and Core Web Vitals: The Hidden Performance Tax on Shopify Product Pages

Wishlist apps add a 'save for later' heart icon — a small UX feature with an outsized performance footprint. The breakdown of how wishlist apps affect LCP, INP, and CLS on product pages, and the audit pattern for keeping the feature without paying a 200-400ms speed tax.

StoreVitals Team

Wishlist functionality looks like a tiny feature: a heart icon next to "Add to Cart" that lets shoppers save products for later. The merchandising team asks for it, a Shopify app gets installed in five minutes, and the icon appears. What doesn't appear on the install screen is the 150-350KB of JavaScript, the 2-4 additional network requests per product page, and the 80-200ms LCP regression that often follows.

Wishlist apps are one of the most-installed app categories on Shopify — Wishlist Plus, Smart Wishlist, Wishlist Hero, Growave, and Swym Wishlist Plus are common picks — and they're also among the most-overlooked performance liabilities. The pattern below explains exactly where the cost comes from and how to keep the feature without paying it.

Where the Performance Cost Comes From

A wishlist app does three things technically:

  1. Renders a heart icon on every product card and product page
  2. Tracks the wishlist state — typically in localStorage for guests, and on the app's servers for logged-in customers
  3. Fires events when items are added, removed, or shared

To do these, the app injects a JavaScript bundle into the storefront via Shopify's app injection mechanism. Bundle sizes vary widely but typically run 80-300KB minified and gzipped. The bundle loads on every page — including the homepage, collection pages, and product pages — because the heart icon needs to appear on collection cards as well as product detail pages.

Three secondary costs follow:

  • Synchronous wishlist state lookups. Many apps fire an API call on page load to check whether the current product is wishlisted by the current user. This adds 100-300ms to the page critical path before the heart icon can render.
  • Render-blocking initialization. Some apps inject their script with no async or defer attributes, blocking HTML parsing until the script downloads and executes.
  • Layout shift from late-rendering icons. If the heart icon is injected after the product card paints, the surrounding elements shift to make room — a CLS contribution of 0.02-0.15 depending on collection density.

Measuring the Impact on Your Store

Before installing or removing a wishlist app, take a baseline measurement on the affected page types:

  1. Open Chrome DevTools → Lighthouse → Mobile, Performance, Cold Run
  2. Run on the homepage, a collection page, and a product page
  3. Record: LCP, INP (or TBT as a proxy), CLS, Total Bundle Size, Total Requests
  4. Install or remove the app
  5. Re-run with the same conditions
  6. Compare. The delta is the app's real-world cost.

Typical findings from StoreVitals audits across Shopify stores running wishlist apps:

  • LCP regression: 80-200ms on product pages, 50-150ms on collection pages
  • INP regression: 30-80ms (the heart icon's click handler often runs synchronously)
  • CLS contribution: 0.02-0.08 from late-loading icons
  • Bundle size addition: 150-400KB across all storefront pages
  • Additional requests: 2-5 per page (script, CSS, state lookup, sometimes a third-party font)

The Five-Point Wishlist Performance Audit

If the wishlist app is staying, these five optimizations recover most of the lost performance.

1. Defer Wishlist Script Loading

Wishlist scripts are not LCP-critical. The heart icon can appear 1-2 seconds after the product image and customers won't notice. Force the script to load with defer or move it to the footer:

// Instead of injection in <head>:
<script src="/apps/wishlist/wishlist.js" defer></script>

// Or move the script tag to just before </body>

This single change typically recovers 50-100ms of LCP on mobile with no functional impact.

2. Lazy-Initialize Below-the-Fold Heart Icons

Most wishlist apps render heart icons immediately on every product card. On a collection page with 24 products, that's 24 icon initializations during initial paint. Use Intersection Observer to lazy-initialize:

// Initialize wishlist hearts only when they enter the viewport
const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      initWishlistButton(entry.target);
      observer.unobserve(entry.target);
    }
  });
});
document.querySelectorAll('[data-wishlist-button]').forEach(el => observer.observe(el));

Initial paint runs only the visible icons (typically 4-6), saving 100-300ms of JavaScript execution time on collection pages.

3. Cache Wishlist State Locally

For logged-in customers, many apps fetch wishlist state on every page load. This is wasteful — the state changes rarely. Cache it in localStorage with a 5-minute TTL:

function getWishlistState() {
  const cached = JSON.parse(localStorage.getItem('wishlist_state') || 'null');
  if (cached && Date.now() - cached.fetchedAt < 300000) {
    return Promise.resolve(cached.data);
  }
  return fetch('/apps/wishlist/state').then(r => r.json()).then(data => {
    localStorage.setItem('wishlist_state', JSON.stringify({ data, fetchedAt: Date.now() }));
    return data;
  });
}

Eliminates 100-300ms of API latency on repeat visits.

4. Reserve Space for Heart Icons to Prevent CLS

The CLS contribution from wishlist icons comes from cards shifting when the icon appears. Reserve the space in CSS:

.product-card__wishlist {
  width: 32px;
  height: 32px;
  display: inline-block;
}
/* Even before icon JS loads, the space is reserved */

This drops the wishlist app's CLS contribution to near-zero.

5. Audit the App's Third-Party Dependencies

Some wishlist apps load their own font, analytics tracker, or chat widget integration. Open Network tab in DevTools after installing the app and look for requests to third-party domains you didn't authorize. Common offenders:

  • Font loaders (Google Fonts, Typekit) for icon fonts
  • Analytics endpoints (Mixpanel, Segment)
  • Tag manager scripts
  • Chat widget integrations

Most apps offer options in their admin to disable these — turn off what you don't use.

When to Remove the Wishlist App Entirely

If wishlist usage in your analytics is <1% of sessions (the typical range is 0.3-2%), the feature is not earning its performance cost. Consider:

  • Native browser bookmarks — your customers already have a way to save pages for later
  • "Save for later" in cart — most Shopify themes support this natively, no app needed
  • Email "back in stock" alerts — captures the same intent (interest in a specific product) with no performance cost on storefront pages

Quick Audit Checklist

  1. Wishlist script loaded with defer or in footer
  2. Below-the-fold heart icons lazy-initialized via Intersection Observer
  3. Wishlist state cached in localStorage with TTL
  4. Heart icon space reserved in CSS (no CLS contribution)
  5. Third-party dependencies disabled in app admin (fonts, analytics, etc.)
  6. Total app bundle < 100KB after optimization
  7. Wishlist add-to-cart conversion measured against control (does the feature actually drive revenue?)

Wishlist is a fine feature when it earns its weight. The pattern that fails is installing the app, getting the heart icon, and never measuring what it costs. StoreVitals scans surface third-party app bundle sizes, render-blocking scripts, and CLS contributors so you can spot wishlist app drift before it shows up as a Core Web Vitals failure in Search Console.

wishlist appsCore Web VitalsShopify performanceproduct pageLCPINP

See these issues on your store?

Run a free scan and find out in seconds.

Run Free Scan