FloraSoul India had a problem that's more common than most D2C founders realise: a visually stunning Shopify store with a mobile conversion rate that didn't reflect the quality of the product or the brand.
When they came to us, the store had been live for several months. Traffic was healthy. The product photography was excellent. The brand story was compelling. But mobile conversion sat below 1%.
We ran the audit. Within 20 minutes, we'd identified four issues that any experienced frontend engineer would spot immediately. None of them required a redesign. None required switching themes or moving to headless. They required clean engineering — the kind that page builder tools and agency generalists tend to leave undone.
The outcome: +41% mobile conversion lift after fixes were shipped. This tutorial walks through exactly what we found, why each issue mattered, and the specific code fixes we applied.
The Diagnostic Stack We Use
Before writing a single line of fix code, we run a structured diagnostic. Three tools, in order:
1. Google PageSpeed Insights — Field data (CrUX) shows real-user performance. Lab data shows what's fixable. The "Opportunities" and "Diagnostics" sections are your starting point. Always test the product page URL, not just the homepage — product pages are where mobile conversion happens.
2. Shopify Theme Inspector — A Chrome extension that profiles Liquid template render time section-by-section. It tells you which parts of your theme are slow to render on Shopify's servers, before a single byte hits the browser. Most developers never use this. It's the Shopify-specific tool that cuts hours off a performance audit.
3. Chrome DevTools Performance tab — For deep INP and long task analysis. Record a typical user interaction (page load → product page → add to cart) and review the flame chart. Long tasks blocking the main thread for 50ms+ are your INP killers.
As a Shopify Partner, we also have access to Shopify's internal theme performance guidelines — which give us a higher ceiling on what "optimised" actually means for a given theme architecture.
Issue 1: Render-Blocking Animation Library
What we found: FloraSoul's theme was loading an animation library (animate.css + a custom JS animation controller) synchronously in the <head> of theme.liquid. The library was being used for subtle product card entrance animations — a nice visual touch, but not critical to the page rendering path.
Why it matters: Scripts and stylesheets loaded synchronously in <head> block the browser from rendering anything below them until they've fully downloaded, parsed, and executed. On a mobile device on a 4G connection, a 180KB animation library can add 1.2-2 seconds to Time to First Contentful Paint. The user stares at a blank screen.
The fix: We moved the animation script to load with defer, which tells the browser to download it in parallel but not execute it until after the page has parsed. We also lazy-loaded animate.css via a <link rel="preload"> with onload swap:
<!-- Before: blocking -->
<link rel="stylesheet" href="animate.min.css">
<script src="animations.js"></script>
<!-- After: non-blocking -->
<link rel="preload" href="animate.min.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="animate.min.css"></noscript>
<script src="animations.js" defer></script>
Result: FCP dropped by approximately 1.4 seconds on mobile. The animations still work — they initialise ~300ms after page paint, which is imperceptible to users.
Issue 2: LCP Image Not Preloaded
What we found: The hero product image on every product page — the Largest Contentful Paint element — was being loaded as a regular <img> tag with loading="lazy". This is one of the most common LCP performance mistakes we encounter, and it directly contradicts Google's guidance.
Why it matters: loading="lazy" tells the browser to defer loading this image until it's near the viewport. For the LCP image, which is by definition in the viewport on load, this delays the most important paint event on the page. Combined with no preload hint, the browser discovers the LCP image only after it has parsed the full HTML — wasting critical connection time.
The fix: We identified the LCP image in the product template (sections/main-product.liquid) and added an explicit preload hint in the <head> and removed loading="lazy" from the primary image:
{% comment %} In theme.liquid <head>, injected via section schema: {% endcomment %}
{% if template == 'product' %}
<link
rel="preload"
as="image"
href="{{ product.featured_image | img_url: '1200x' }}"
imagesrcset="{{ product.featured_image | img_url: '400x' }} 400w,
{{ product.featured_image | img_url: '800x' }} 800w,
{{ product.featured_image | img_url: '1200x' }} 1200w"
imagesizes="(max-width: 768px) 100vw, 50vw"
>
{% endif %}
And in the product image template, the primary image:
<img
src="{{ product.featured_image | img_url: '800x' }}"
srcset="{{ product.featured_image | img_url: '400x' }} 400w,
{{ product.featured_image | img_url: '800x' }} 800w,
{{ product.featured_image | img_url: '1200x' }} 1200w"
sizes="(max-width: 768px) 100vw, 50vw"
alt="{{ product.featured_image.alt | escape }}"
width="800"
height="800"
fetchpriority="high"
{%- comment %} No loading=lazy on LCP image {%- endcomment %}
>
Secondary images in the gallery kept loading="lazy" since they're below the fold.
Result: LCP on mobile moved from 4.1 seconds to 2.3 seconds — crossing the "Good" threshold for the first time.
Issue 3: Oversized Product Images Without srcset
What we found: All product images were being served at their original upload resolution — averaging 2400x2400px at 1.8MB per image. Mobile devices were downloading desktop-sized images and then scaling them down via CSS. On a product page with 6 images, that's over 10MB of image data downloaded before the user has seen a single product detail.
Why it matters: Image weight is the single largest contributor to slow LCP and high Total Blocking Time on Shopify product pages. Shopify's CDN serves WebP automatically, but it serves the full resolution unless you specify a resize in the URL. Without srcset, every device gets the largest image.
The fix: We applied srcset across all product image elements (the gallery, thumbnails, and zoom view), using Shopify's built-in image URL filter to request device-appropriate sizes:
{%- for image in product.images -%}
<img
src="{{ image | img_url: '600x' }}"
srcset="{{ image | img_url: '300x' }} 300w,
{{ image | img_url: '600x' }} 600w,
{{ image | img_url: '1200x' }} 1200w"
sizes="(max-width: 480px) 90vw,
(max-width: 900px) 50vw,
40vw"
alt="{{ image.alt | escape }}"
width="600"
height="600"
loading="{% if forloop.first %}eager{% else %}lazy{% endif %}"
>
{%- endfor -%}
Note the forloop.first logic: the first image gets loading="eager" (matching the fetchpriority="high" LCP fix), all subsequent gallery images get loading="lazy".
Result: Median image payload on product pages dropped from 10.2MB to 1.4MB on mobile. Visible page weight in DevTools Network tab went from 12.8MB to 2.1MB total for an average product page load.
Issue 4: Third-Party Tracking Scripts Firing Synchronously
What we found: FloraSoul had five tracking integrations installed: Facebook Pixel, Google Analytics 4, a heatmap tool, an email capture popup, and a WhatsApp chat widget. All five were being injected into <head> as synchronous scripts, either by apps or by manual snippet additions to theme.liquid.
Why it matters: Each synchronous third-party script is a single point of failure for your entire page render. If any one of these services has a slow response (which happens regularly with ad tracking endpoints), your page waits. Combined, five synchronous tracking scripts were adding 800ms-1.2 seconds of network round-trip time before the browser could paint anything.
The fix: We consolidated all third-party scripts under Google Tag Manager with the GTM container loading async. Within GTM, we configured all tags to fire on DOM Ready or Window Loaded events, not Page View. This decouples the tracking instrumentation from the critical rendering path entirely:
<!-- In theme.liquid <head>: only GTM loads here -->
<script>
(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-XXXXXXX');
</script>
All five tracking scripts were then moved into GTM and configured to fire post-render. The WhatsApp widget in particular was set to trigger only after the first user interaction (click event), eliminating its zero-interaction load cost entirely.
Result: Total Blocking Time dropped from 1,840ms to 290ms on mobile — crossing the "Good" threshold. The INP score moved from "Needs Improvement" to "Good" across most user sessions.
The Combined Result
These four fixes shipped over two development sprints. No redesign. No theme switch. No Hydrogen migration (our comparison of Liquid vs Hydrogen architecture covers when that conversation becomes relevant). Just clean engineering on an existing Liquid store.
Before:
- Mobile LCP: 4.1s
- Total Blocking Time: 1,840ms
- Image payload per product page: 10.2MB
- Mobile PageSpeed score: 31
After:
- Mobile LCP: 2.3s
- Total Blocking Time: 290ms
- Image payload per product page: 1.4MB
- Mobile PageSpeed score: 78
Business outcome: +41% mobile conversion rate lift over the following 8 weeks. FloraSoul also saw a +28% improvement in average order value, which we attribute partly to faster load enabling users to browse more products per session.
What To Do Next For Your Store
If you want to run this same diagnostic on your store, start with our full Shopify SEO checklist — it covers all the CWV items in audit format with a priority ordering for which fixes to address first.
If you'd rather have our team do it, we offer full Shopify performance audits and implementation through our Shopify development service. We use the exact diagnostic process and fix patterns above on every store we touch. As a DPIIT-recognised startup and official Shopify Partner, we combine the credibility of a verified agency with the technical depth of an engineering team that has shipped 50+ production Shopify builds.