Shopify Hydrogen Blog

How I Cut a Hydrogen Homepage From 5s to 2s

By Emre Mutlu, creator of the world's first English Shopify Hydrogen course on Udemy.

Published April 21, 2026Last updated April 21, 20266 min read

TL;DR

I cut a Shopify Plus Hydrogen homepage from roughly 4 to 5 seconds to about 2 seconds by stopping the initial useEffect fetch, server-rendering only the first tab of each section, and lazy-loading the rest on interaction. The bigger win was not speed alone. It was getting product HTML back into the first response.

Hydrogen performance diagnostics visual with server-rendered product grids, cache layers, optimized media tiles, and timing panels.

Production note template

Problem, symptom, root cause, fix, risk, and related links.

Problem

A Hydrogen homepage can render a fast shell while delaying the commerce content shoppers need.

Symptom

Product sections appear only after hydration and the initial page feels incomplete.

Root cause

Primary product data was fetched from a client effect instead of the common server-rendered route path.

Fix

Move the first visible product set into route data, cache intentionally, and leave less-common tabs for later interaction.

Risk

If the common path stays client-only, performance, crawlability, and perceived readiness all suffer together.

Related paths

Use the issue library and checklist before the next edit.

I cut a production Shopify Plus Hydrogen homepage from roughly 4 to 5 seconds to about 2 seconds by removing one bad assumption: that every tab on the page needed its product data on first load. It did not. Once I moved the first tab into the route loader and lazy-loaded the rest, both speed and SEO got cleaner.

Why the homepage was slow in the first place

The slowdown came from loading too much data too late. A single homepage section had 12 tabs with 8 products each, which meant 96 products per section before counting multiple sections. All of that was fetched client-side in useEffect, so the page hydrated first, then started the real work.

This was for a Shopify Plus jewelry brand I work with, a homepage with multiple horizontal product rails and filter tabs. From a React point of view, the code looked harmless. From a Hydrogen point of view, it was a footgun, especially if you have not internalized React's You Might Not Need an Effect guidance yet.

// Before, anti-pattern in Hydrogen
function HomepageSection() {
  const [products, setProducts] = useState([]);

  useEffect(() => {
    fetchAllTabsProducts().then(setProducts);
  }, []);

  return <Tabs products={products} />;
}

The practical result was obvious. The homepage took roughly 4 to 5 seconds to feel ready. The SEO problem was quieter. The initial HTML had no product listings at all, because product data only arrived after hydration.

What I changed, and why it worked

I changed the fetch strategy to match real user behavior. Only the first tab of each section is server-rendered with its 8 products. The rest of the tabs lazy-load on click. That cuts initial data weight, gets product HTML into the response, and stops the homepage from paying for tabs most users never open.

I moved the first-tab fetch into the route loader and removed the initial useEffect path entirely. The component now receives the first tab as props from the server response, and only requests other tabs after an actual interaction. If you want the official Hydrogen side of this pattern, Shopify's caching docs are the right reference for the cache layer.

// After, Hydrogen-native
export async function loader({context}) {
  const firstTabProducts = await context.storefront.query(FIRST_TAB_QUERY, {
    cache: context.storefront.CacheCustom(
      // Tuned to the product catalog's update frequency
    ),
  });

  return json({firstTabProducts});
}

function HomepageSection() {
  const {firstTabProducts} = useLoaderData();

  return <Tabs initialProducts={firstTabProducts} />;
}

That one change cut the homepage from roughly 4 to 5 seconds to about 2 seconds in observed behavior. I did not benchmark it with Lighthouse or WebPageTest, so I am not pretending this is a lab-grade performance study. It is a production engineering note. The page felt dramatically lighter because it stopped trying to fetch everything just in case.

The SSR bug that almost made this change look finished when it wasn’t

The first deploy still had an SSR hole. The in-house team tested the homepage with JavaScript disabled and found that the first-tab products were still missing from the initial HTML. That meant the SSR fix was not actually fully server-side yet, even though the page looked correct in a normal browser session.

The root cause was leftover client-only logic. The first tab had been conceptually moved to the loader, but one residual branch still routed its fetch through the old client path. So the UI worked, but the HTML response was still incomplete.

The fix was simple once the test exposed it. I made sure the first-tab data was fetched in the route loader and passed straight into the component as props, with no fallback path that depended on hydration. After that, the JS-disabled test showed product listings in the HTML exactly where they should be.

This is why I keep telling teams that it works in the browser is not a real SSR test. If the route matters for SEO, disable JavaScript once before you call it done. It is the fastest sanity check you can run.

The small UI cleanup that broke context after the performance fix

Performance changes often create pressure to compress the UI, and that can quietly remove meaning. In this case I stripped category prefixes from tab labels so long titles fit better inside horizontal rails. The result looked cleaner, but one section lost enough context that the shortened label became confusing.

A good example was the chain section. Titles like Diamond Chains and Gold Chains were shortened to make the tabs visually lighter. After deploy, the team noticed that under the Gold Chains section, the label just read Chains. It was shorter, but it also felt detached from the parent context.

The fix was to add a conditional guard rail. Prefix stripping stayed in place for titles where the parent section already carried the right context, but not in cases where shortening the label made the tab feel generic. That was a small change, but it mattered. Faster UI is not automatically clearer UI.

I mention this because performance work often gets discussed as if it is separate from interface language. It is not. Every time you compress data, text, or layout, you risk dropping context that the user still needed.

What I would tell another Hydrogen developer before they copy this pattern

If your first instinct is to preload every tab on a tabbed homepage, stop and check whether real users justify that cost. Most do not. They look at the first tab, some click the second, and very few make their way through all 12 tabs across multiple sections. Optimize for the common path, not the paranoid one.

If you are fetching primary route data in useEffect on a Hydrogen page, you are usually fighting the framework. Hydrogen gives you route loaders for a reason. Use the server for the part of the page that must exist on first load, then lazy-load the rest behind real user intent.

If you need more background before making that decision, my Should I Use It? page explains where Hydrogen is justified and where it is not. My cost breakdown is the practical version of the same conversation, and the case studies page shows the kind of stores where these tradeoffs actually appear.

The larger lesson is that this was not really a Hydrogen problem. It was a React habit problem. Teams coming from SPA-heavy or Next.js-heavy workflows reach for useEffect because it feels familiar. In Hydrogen, that habit gets expensive fast. You lose SSR, you lose HTML visibility, and you slow down the exact page that should be easiest to trust.

FAQ

Why was useEffect the wrong place for this homepage fetch?
Because it pushed primary page data to the client after hydration. That delayed the moment the homepage felt ready and removed product listings from the initial HTML, which is the wrong tradeoff for a category-heavy storefront.

What changed after moving the first tab to the route loader?
The first tab became part of the server-rendered response. That put real product HTML in the initial response, improved the ready state, and passed the simplest SSR check, loading the page with JavaScript disabled.

Should every tab be preloaded on a Hydrogen homepage?
Usually no. Most users interact with the first tab, some click the second, and very few open every tab in every section. Preloading everything is often an expensive guess that hurts the common path.

Where does intentional cache tuning fit in this pattern?
It matters when collection freshness and edge caching need to match catalog behavior. The bigger point is not one exact number. It is moving the first tab into the loader and caching the query deliberately instead of relying on a client-side effect.

Internal links

Where this topic connects across the site.

External references

Official sources behind the technical framing.

Your Shopify store works, but every new feature takes 3x longer than last year? That is when I come in. If your homepage, collection, or product pages are hitting the ceiling of what your current stack can deliver, I can help you see whether Hydrogen is the right move, and if it is, how to implement it without these traps.

Related guides

Related issues, templates, and next steps.

Next Step

Let’s scope the lean Hydrogen storefront you actually need.

If this production note sounds like your store's situation, I can help you turn the insight into a clear Hydrogen scope and launch plan.

Send an email brief

Direct senior access. No fake agency layer.

Owned lead capture

Request a Hydrogen Scope Review

Send the required fields first. Add design status, product count, budget, and timeline only if they are already clear.

I do not sell Hydrogen if Liquid is the better move.

Short brief path

Required: name, email, store URL or brand, and main problem. Start with:

  • Store URL or brand
  • What feels blocked
  • Budget and timeline, if you know them
Which features are needed?

Your details are used only to reply to this project inquiry. No newsletters, no list sharing. If this is a small theme tweak, I will usually point you to a lighter option.