Free guide · about 2 hours on a real store
Make a Shopify store fast
Let's speed up a Shopify store together, one step at a time. I will tell you what to open, what to look for, what to change, and how to check it worked. Written for the Horizon theme.
Before we start
Think of a slow store like a shop with a jammed door. People walk up, push, wait… and leave. Our job today is to find what is jamming the door, and remove it.
We will do it in 10 small steps. After every step, you check that it worked before moving on.
| You need | Why |
|---|---|
| A Shopify store using Horizon | All click paths here are for Horizon |
| Google Chrome | For DevTools and Lighthouse |
| A copy of your live theme | So you never break the real store |
| A notebook (or a spreadsheet) | To write the numbers before and after |
Three words you will hear a lot
- LCP (Largest Contentful Paint): how fast the biggest thing on the screen shows, usually the hero image or the product photo. Good: 2.5 seconds or less.
- CLS (Cumulative Layout Shift): how much the page jumps while it loads. You know the feeling: you go to tap a button and it moves. Good: 0.1 or less.
- INP (Interaction to Next Paint): how fast the page reacts when you tap or click. Good: 200 ms or less. (It replaced the old "FID" score.)
Step 1: Measure first (your "before" photo)
Before a diet, you step on the scale. Same here: we measure first, so we can prove the work helped.
Pick 4 pages
Test the 4 pages that make money:
- The home page
- One collection page
- One product page
- The cart page
Run the tests
- Open pagespeed.web.dev (opens in a new tab), paste your home page link, and click Analyze.
- Look at the Mobile tab first. Most shoppers are on phones.
- Repeat for the other 3 pages.
- Also open the page in Chrome, press F12, open the Lighthouse tab, pick Mobile, and click Analyze page load.
Write these 5 things down
| Page | Score | LCP | CLS | What is the LCP element? |
|---|---|---|---|---|
| Home | ||||
| Collection | ||||
| Product | ||||
| Cart |
The LCP element is listed in Lighthouse under Largest Contentful Paint element. It is usually the hero image or the main product photo. Remember it. We fix it in Step 9.
Also check Shopify's own report from real shoppers: open Shopify admin → Online Store. The Web performance card shows LCP, INP and CLS. Click a number to see the full report. (You can also find it in Analytics → Reports: search for contentful.)
Step 2: Make the images lighter
Images are usually the heaviest part of a store. A 3 MB banner is like carrying a suitcase when you only need a wallet.
Find the heavy images
- Open the page in Chrome and press F12.
- Click the Network tab, then the Img filter.
- Reload the page.
- Click the Size column to sort from biggest to smallest.
Aim for these sizes
| Image | Good size |
|---|---|
| Desktop banner | under 250–300 KB |
| Mobile banner | under 150–200 KB |
| Product photo | about 150–250 KB |
Before uploading, compress big images with a free tool like squoosh.app (opens in a new tab). Use JPG or WebP for photos. Use PNG only for logos or images that need a see-through background.
What Horizon already does for you
Good news: Horizon already makes smaller copies of every image for phones (this is called srcset), and it already lazy-loads images lower on the page. So for Horizon's own sections, uploading a good image is enough.
In your own custom code
If you write your own section or Custom Liquid block, print images like this:
{{ product.featured_image | image_url: width: 1200 | image_tag:
widths: '375, 750, 1100',
sizes: '(min-width: 990px) 50vw, 100vw',
loading: 'lazy' }}image_url: width: 1200asks Shopify for a resized copy, not the huge original.widthsandsizeslet the phone pick a small copy and the laptop a bigger one.loading: 'lazy'means "load it only when the shopper scrolls near it".
Step 3: Clean up apps and outside scripts
Every app can add its own code to every page, even apps you stopped using. It is like old furniture left in a small room.
Remove what you do not use
- Open Shopify admin → Apps.
- For each app, ask: "Did we use this in the last month?" If not, uninstall it.
- Open Online Store → Themes → Customize → App embeds (the puzzle icon on the left). Turn off embeds you do not need.
Find leftover code
Uninstalled apps sometimes leave code behind.
- Open Online Store → Themes → next to your copy click ⋯ → Edit code.
- Open layout/theme.liquid.
- Search (Ctrl + F) for
<script. - For each script, ask: "Which app or tool is this? Do we still use it?"
- Also look in the snippets folder for files named after old apps.
If you still need a script but it is not important for the first screen (heatmaps, chat, extra trackers), add defer:
<script src="https://example.com/widget.js" defer></script>defer means: "download it in the background and run it after the page is ready". The shopper sees the page first.
Replace heavy apps with theme features
Horizon already has many things people install apps for:
- Trust icons and payment logos → the Icon and Payment icons blocks
- Low stock message → the Product inventory block
- Before/after slider → the Comparison slider block
- Simple text badges and popups → a Custom Liquid block with a few lines
Step 4: Keep CSS small
CSS is the paint and decoration of the page. The browser waits for it before it shows anything, so less is better.
- Press F12 → Network → CSS, and reload.
- Look for CSS files from old apps (their names often include the app name). Remove the code that loads them in
layout/theme.liquidor the app's snippet. - In your own blocks, put CSS inside a
{% stylesheet %}tag at the bottom of the block file, the same way Horizon does. Shopify then loads it only where the block is used.
{% stylesheet %}
.usp-list { margin: 0; padding-left: 18px; }
{% endstylesheet %}Use the theme's colours
Do not type colours like #ff0000 again and again. Horizon turns your Theme settings → Colors into CSS variables. Use them, so one change updates the whole store:
.my-badge {
background: var(--color-primary-button-background);
color: var(--color-primary-button-text);
}If a variable name does not work, open snippets/theme-styles-variables.liquid to see the exact names in your Horizon version.
Step 5: Keep JavaScript light
JavaScript is what makes things move: sliders, popups, the cart drawer. It is also the main reason a page feels "stuck" when you tap (that is INP).
Find the big files
- Press F12 → Network → JS, and reload.
- Sort by Size.
- For each big file, ask: "Which feature is this? Do we need it on this page?"
Simple rules
- Remove sliders, galleries and popups nobody uses.
- One tool per job. Three different slider libraries for three sliders is waste. Use one.
- Load your own scripts with
defer:
<script src="{{ 'custom.js' | asset_url }}" defer></script>- Send only the data you need. Do not print a huge JSON of every product into the page when the script needs only the title and price.
- No jQuery. Horizon does not use it. If an old script needs jQuery, rewrite it in plain JavaScript or remove it.
Step 6: Fonts
Every font style is a separate download. Five fonts with four weights each is twenty downloads before the text looks right.
Keep it simple
- 1 or 2 font families.
- 2 or 3 weights (for example regular, medium, bold).
In Horizon, pick fonts in Customize → Theme settings → Typography. Shopify hosts these fonts and Horizon already loads them the fast way, so the text shows straight away in a backup font, then swaps.
If you upload your own font
Upload the .woff2 file in Edit code → Assets. Then open layout/theme.liquid and add this inside <head>. It must be in a .liquid file, because asset_url is Liquid:
<style>
@font-face {
font-family: 'BrandFont';
src: url('{{ "brand-font.woff2" | asset_url }}') format('woff2');
font-display: swap;
}
</style>font-display: swap means "show the text right away in a normal font, then switch to mine when it arrives". The shopper never stares at blank space.
To start the download early, add this inside <head> in layout/theme.liquid:
{{ 'brand-font.woff2' | asset_url | preload_tag: as: 'font', type: 'font/woff2', crossorigin: 'anonymous' }}Preload only one font file, the one used in the first screen. Preloading many slows things down again.
Step 7: Clean up Liquid and sections
Liquid runs on Shopify's server before the page is sent. Heavy loops make the server work longer, so the first byte arrives later.
What to look for
- Big loops over everything. This line reads every product in the store:
{% for product in collections['all'].products %}Use a small, named collection with a limit instead:
{% for product in collections['best-sellers'].products limit: 8 %}all_products['handle']used many times. Each one is a lookup. Use a product list metafield or a collection instead.- Sections nobody uses. In each JSON template (Customize → each page type), remove hidden sections. A hidden section still sits in the template.
- Old files. In
sections/andsnippets/, files from old apps or old designs can go. Before deleting, search the theme for the file name to make sure nothing uses it.
Step 8: Let Shopify do the heavy lifting
Shopify stores the finished pages close to your shoppers, all over the world. Help it:
- Keep pages the same for everyone. Things like a random product on every load, or the exact current time printed by Liquid, make each page different and harder to reuse.
- Apps: do the work in the app, not in the browser. If an app needs to calculate something big, it should do it on its own server and send only the final answer to the store.
- Load extra parts only when needed. For example, load "recommended products" only when the shopper scrolls near them. Horizon already does this for its Product recommendations block.
Step 9: Fix the LCP image (the biggest win)
Remember the LCP element you wrote down in Step 1? This is usually the step that moves the score the most.
- Make the image light. Compress it to under about 300 KB (Step 2).
- Do not lazy-load it. It is at the top, so it must load right away.
- Use one image, not a slideshow. A slideshow waits for JavaScript before it shows the first slide. In Customize → Home page, use a single Hero or Image section at the top.
- Keep it high in the page. Do not put heavy sections (videos, sliders, app widgets) above it.
In your own hero code, tell the browser this image is important:
{{ section.settings.image | image_url: width: 1800 | image_tag:
loading: 'eager',
fetchpriority: 'high',
widths: '750, 1100, 1500, 1800',
sizes: '100vw' }}loading: 'eager'means "load now".fetchpriority: 'high'means "this one first, before other images".
Step 10: Measure again and go live
Time for the "after" photo.
- Run PageSpeed Insights and Lighthouse on the same 4 pages, on your theme copy (use the theme's Preview link).
- Fill the same table again, 3 runs each, middle score.
- Compare before and after.
- Click through the store like a shopper: add to cart, open the cart, change quantity, go to checkout. Make sure nothing broke.
- When everything works, publish the copy: Online Store → Themes → your copy → Publish.
- Check the Web performance card in Shopify admin → Online Store again after a few days. It uses real shoppers, so it updates slowly.
| Page | Score before | Score after | LCP before | LCP after |
|---|---|---|---|---|
| Home | ||||
| Collection | ||||
| Product | ||||
| Cart |
Common mistakes
- Testing the live theme while you edit. Always work on a copy.
- Testing once. Lighthouse moves up and down. Use the middle of 3 runs.
- Lazy-loading the hero image. It makes LCP worse, not better.
- Deleting a file without searching for it first. Search the theme for the file name before you delete it.
- Chasing 100. A real store with real apps rarely gets 100. Aim for clearly better LCP, CLS and INP.
Quick recap
- Measure 4 pages first and write the numbers down.
- Make images light. Horizon handles sizes for you.
- Remove unused apps and leftover scripts. Defer the rest.
- Keep CSS small and use the theme's colour variables.
- Keep JavaScript small. One tool per job,
defer, no jQuery. - Use 1–2 fonts with
font-display: swap. - Avoid big Liquid loops and unused sections.
- Keep pages the same for everyone, so Shopify can serve them fast.
- Fix the LCP image: light, not lazy, no slideshow, high priority.
- Measure again, test like a shopper, then publish.
Want me to look at your store with you? Book a live class and we will go through these steps on your own theme.