Skip to main content

Performance best practices for Shopify themes

Performance is an important factor for merchants when they choose a theme for their online store. When you build or customize a theme, build with performance in mind. Optimizing your theme for performance is key to the success of the merchants you support and to the experiences of their customers. Performance directly influences conversion rates, repeat business, and search engine rankings.

When you submit a theme to the Shopify Theme Store, Shopify tests it on a benchmark store to determine its performance score. To be accepted into the Shopify Theme Store, a theme must have a minimum average Lighthouse performance score of 60 across the home page, product page, and collection page. You can run a similar test on your theme using a development store.

Learn more about performance testing for themes in the Shopify Theme Store.

Each best practice page answers three questions: Why does this matter technically? How do you implement it? And how do you test that it worked?


Specific, single-subject, actionable recommendations:

Methodology, measurement, and broader strategy:


Each recommendation table below has a Metric column naming the metric that the recommendation moves. Those metrics are abbreviated throughout this section, in roughly the order a page load produces them:

  • Time to First Byte (TTFB): How long the browser waits for the first byte of the HTML response. On a Shopify storefront, most of that time is Shopify rendering your Liquid, so TTFB is the metric your server-side work moves. Nothing else can start until the response arrives.
  • First Contentful Paint (FCP): When the browser paints the first text or image. Render-blocking stylesheets and scripts in the <head> are the usual reason it's late.
  • Largest Contentful Paint (LCP): When the largest element in the initial viewport finishes rendering, usually a hero or product image. It tracks perceived load speed more closely than any other single metric.
  • Cumulative Layout Shift (CLS): How much visible content moves unexpectedly while the page loads. Images without dimensions, fonts that swap in late, and app-injected content are the common causes.
  • Interaction to Next Paint (INP): How long the page takes to respond visibly after someone taps or clicks. Main-thread work, usually JavaScript, is what makes it slow.

LCP, CLS, and INP are the Core Web Vitals (CWV), which Google uses as a ranking signal. TTFB and FCP aren't scored, but they're diagnostic: a slow LCP alongside a slow TTFB points at your Liquid, while a slow LCP with a healthy TTFB points at the browser-side work that follows. See Debugging with metric gaps for how to read those gaps.


These best practices have the largest impact on Core Web Vitals across the broadest range of themes. Check these before anything else.

RecommendationSummaryMetricImpact
Never lazy-load the LCP imageloading="lazy" on the LCP image delays it until after layout completes. Always load images visible in the initial viewport eagerly.LCPHigh
Mark the LCP image with fetchpriority="high"Tells the browser to fetch the LCP image before other resources. A one-line change with measurable LCP improvement.LCPHigh
Don't hide the LCP image behind animationsFade-in and reveal animations delay the LCP event even after the image finishes downloading.LCPHigh
Render essential content in Liquid and HTML, not JavaScriptMoving product info, hero content, or nav into client-side JavaScript delays LCP and harms SEO. Liquid renders server-side before the browser does anything.LCPHigh
Avoid deeply nested Liquid loopsLoops nested across products, variants, or options grow quadratically with catalog size. A theme that's fast on 10 products can be slow on 100. The most common cause of high TTFB.TTFBHigh
Use preload resource hints sparinglyOverusing preload competes with the browser's own prioritization and can make pages slower. Reserve it for only 1 or 2 resources the browser discovers late.LCPMedium

Shopify renders Liquid templates server-side on every page request. The time Shopify spends executing your Liquid code directly determines Time to First Byte (TTFB), the foundation on which all other performance metrics are built. Slow TTFB delays every subsequent phase: the browser can't start parsing HTML, discovering images, or executing scripts until the server finishes rendering. Client-side optimizations don't reduce TTFB, but reducing Liquid rendering time does.

Common sources of Liquid slowness include loops that grow in cost as catalog size increases, especially nested loops and metafield access inside loops, unnecessary database queries caused by iterating variants or products beyond what's displayed, and expensive operations that repeat work on every iteration. Run the Shopify Theme Inspector for Chrome to identify which Liquid lines are slowest in your theme before optimizing.

RecommendationSummaryMetricImpact
Avoid deeply nested Liquid loopsEliminate O(n²) patterns, including nested loops and metafield access inside loops, that make rendering time grow quadratically with catalog size.TTFBHigh
Move metafield access outside loopsMove metafield access outside loops, avoid variant-level metafield checks in loops, and prefer theme settings over metafields for static data.TTFBHigh
Avoid over-fetching product variantsLoad only the variant data needed for the initial render. Iterating all variants forces expensive database queries.TTFBHigh
Limit pagination depthKeep pagination under 25,000 objects. Deeper pagination is resource-intensive and slows all requests on the server.TTFBMedium
Flatten nested render calls, especially inside loopsMinimize render and include nesting, especially inside loops. Each nested snippet adds rendering overhead.TTFBMedium
Defer dialog content loadingGuard expensive Liquid in dialogs and drawers with conditional parameters so content only loads when the component opens.TTFBMedium
Defer child product loading in combined listingsUse product.options_with_values for combined listing UI, and defer child product data through the Section Rendering API.TTFBMedium
Filter collections before entering loopsPre-filter collections before entering loops. Conditional logic inside loops multiplies overhead across every iteration.TTFBMedium
Move repeated assign and filter calls outside loopsAssign calculations and filter operations to variables before loops so they run once, not once per iteration.TTFBMedium
Load above-the-fold and below-the-fold sections differently with section.indexUse section.index and section.location to apply different loading strategies based on whether a section is in or outside the initial viewport.LCPMedium
Use the Section Rendering API for dynamic updatesFetch and update individual theme sections through Ajax using the Section Rendering API instead of full page reloads.TTFBMedium
Cache repeated Liquid filter results with assignCache filter results with assign instead of calling the same filter multiple times with the same arguments.TTFBLow
Assign block.settings to a variableAssign block.settings to a variable instead of accessing it repeatedly to eliminate redundant object lookups.TTFBLow
Output strings with echo instead of append and prependUse echo or {{ }} output tags instead of string manipulation filters like append or prepend.TTFBLow
Limit how many items a Liquid array fetchesA for loop's limit reduces the fetch for collection.products and collections. Other arrays need paginate.TTFBHigh

Images are typically the Largest Contentful Paint (LCP) element, the metric that measures when the largest visible content finishes loading, and that directly tracks perceived page speed. Correct image loading strategy, including eager or lazy loading, fetchpriority, and responsive sizing, has a direct effect on LCP for most storefronts.

Shopify's image_tag and image_url Liquid filters handle the most complex parts automatically: generating srcset attributes, adding width and height to prevent layout shift, and serving images from the CDN. Using these filters consistently is the baseline. The recommendations below cover the decisions and patterns that go beyond the filters themselves.

RecommendationSummaryMetricImpact
Mark the LCP image with fetchpriority="high"Apply fetchpriority="high" to the LCP image to signal its importance before the browser completes layout.LCPHigh
Never lazy-load the LCP imageApply loading="lazy" only to images outside the initial viewport. Always eagerly load images visible in the initial viewport, especially the LCP candidate.LCPHigh
Don't hide the LCP image behind animationsRemove or async-load CSS transitions on LCP images. Animations delay the LCP event even when the image has loaded.LCPHigh
Don't use CSS background-image for LCP contentUse <img> instead of CSS background images for hero and LCP content so the browser's preload scanner can discover them early.LCPHigh
Serve correctly sized images with srcset and sizesUse srcset and sizes with image_tag to serve the smallest image that still looks good at each viewport size.LCPMedium
Prevent image layout shiftAlways include width and height attributes on <img> tags, or use image_tag, which adds them automatically.CLSMedium
Use image_url and image_tag instead of manual CDN URLsUse image_url and image_tag instead of constructing CDN URLs manually to get automatic optimization and correct attributes.LCPMedium
Prevent dialogs from hijacking LCPKeep dialogs smaller than main content or delay them until after interaction so they don't become the LCP element.LCPMedium
Use Liquid filter chains for imagesGenerate image markup through Shopify's filter system (image_url and image_tag) instead of constructing URLs manually.LCPMedium
Use the picture element when you have separate mobile and desktop imagesReplace CSS-toggled mobile/desktop hero image pairs with a <picture> element so the browser downloads only the matching image.LCPMedium

JavaScript, whether your own theme code or scripts from installed apps, runs on the main thread and directly competes with the browser's ability to render content and respond to user input. Too much JavaScript, loaded at the wrong time or written inefficiently, is one of the primary causes of poor Interaction to Next Paint (INP) and delayed Largest Contentful Paint (LCP).

The goal is to load only what's needed, only when it's needed: defer non-critical scripts, import modules on interaction instead of at page load, and avoid heavyweight frameworks when Liquid and CSS can do the same job. When JavaScript problems do appear, profiling the specific cause matters. Badly written first-party code is just as damaging as a poorly implemented third-party script.

RecommendationSummaryMetricImpact
Remove render-blocking appsIdentify and remove or defer app scripts that block HTML parsing before any content is rendered.LCPHigh
Render essential content in Liquid and HTML, not JavaScriptRender initial content with Liquid and HTML. Use Hydrogen for server-side rendering if a framework is truly required.LCPHigh
Audit and remove unused scriptsAudit all scripts, including apps, tracking pixels, and theme code, and remove anything that isn't earning its performance cost.INPHigh
Use defer and async on non-critical scriptsUse the defer attribute on non-critical scripts. Use async only for independent third-party scripts where execution order doesn't matter.LCPHigh
Load JavaScript on user interactionUse dynamic import() inside event listeners to load JavaScript modules only when a user actually interacts with a component.INPHigh
Remove A/B test anti-flicker snippets when no tests are runningDisable anti-flicker snippets when no tests are running, and end completed tests immediately to remove their performance cost.LCPHigh
Defer render-blocking CSS and JavaScriptUse async/defer for JavaScript and the async CSS pattern for stylesheets that aren't needed for initial render.LCPMedium
Debounce, throttle, and yield in event handlersUse debouncing, throttling, passive listeners, and requestAnimationFrame to reduce main-thread blocking from event handlers.INPMedium
Cancel in-progress view transitions on user interactionCancel in-progress view transitions when users interact to prevent transitions from blocking INP.INPMedium
Load JavaScript modules with import maps instead of bundlersUse import maps with bare module specifiers. Shopify automatically includes es-module-shims, so don't load your own copy.FCPLow
Build DOM for hidden components only when openedFilter panels, cart drawers, and dialogs add thousands of DOM nodes even when closed. Build their DOM only when the user first opens them.INPMedium

CSS affects three Core Web Vitals: First Contentful Paint (FCP) through render-blocking stylesheets, Cumulative Layout Shift (CLS) through font swapping and late-loading content, and INP through expensive style recalculations triggered by too many stylesheets or layout-affecting animations. Web fonts introduce additional complexity because they're a render-blocking resource by default and cause visible layout shift when they swap in.

Key CSS decisions that affect Core Web Vitals: load only critical styles synchronously, use size-adjust and font metric overrides to reduce layout shift during font swap, and prefer transform/opacity animations over layout-property animations.

RecommendationSummaryMetricImpact
Reduce stylesheet countMinimize separate <link> tags for stylesheets. Each additional stylesheet triggers an expensive style recalculation that blocks INP.INPHigh
Reduce CLS from font swappingUse size-adjust and override descriptors on fallback fonts to match web font metrics and reduce layout shift during font swap.CLSHigh
Load critical CSS synchronouslyLoad critical CSS for content visible in the initial viewport synchronously. Use the async CSS pattern only for sections outside the initial viewport where it's proven beneficial.FCPHigh
Self-host web fonts on Shopify CDNUpload web fonts to the Shopify CDN instead of loading from Google Fonts or other external services to eliminate DNS and connection overhead.FCPMedium
Eliminate font loading delays with system fontsUse system fonts to avoid downloading web font files before text can render.FCPMedium
Reserve space for app-injected contentUse App Blocks with min-height CSS to reserve space for app-injected content and prevent CLS when it loads.CLSMedium
Animate with transform and opacity instead of layout propertiesAnimate with transform and opacity rather than layout properties like top or left to avoid triggering relayout on every frame.INPMedium
Merge duplicate mobile and desktop menus into oneUse a single responsive menu with CSS media queries rather than separate desktop/mobile menus that double the DOM size.INPMedium
Ensure compatibility with stylesheet subsettingShopify automatically subsets CSS from {% stylesheet %} tags so each page loads only the styles from its render tree. Ensure each file's classes are used only within that file or files it directly renders.FCPMedium

Anchor to Resource hints and CDNResource hints and CDN

Resource hints instruct the browser to take action on resources before it would discover them naturally: establishing connections early, fetching critical assets ahead of time, or prerendering likely next pages. Shopify handles many of these automatically: assets served from the Shopify CDN benefit from HTTP/3, Brotli compression, and global edge caching without any configuration.

The recommendations here cover the decisions that remain in your hands: which third-party origins to preconnect to, when (and when not) to use preload, and how to configure the Speculation Rules API for faster navigations. A key caution applies to all resource hints: overuse is as harmful as underuse, because hints that compete with the browser's own prioritization can make pages slower.

RecommendationSummaryMetricImpact
Avoid request proxiesDon't serve your Shopify storefront through a reverse proxy. Proxies mask real problems, distort metrics, and add latency.TTFBHigh
Serve assets from Shopify CDNServe all theme assets through the Shopify CDN by placing them in the /assets folder to avoid extra DNS connections per external domain.TTFBMedium
Use preload sparinglyUse <link rel="preload"> for only 1 or 2 critical resources the browser discovers late. Overuse competes with browser prioritization.LCPMedium
Speed up navigations with the Speculation Rules APIDeclare prefetch and prerender rules for likely next-page navigations to make them near-instant for users.TTFBMedium
Warm up third-party connections early with preconnectEstablish DNS, TCP, and TLS connections to critical third-party domains earlier with preconnect resource hints.LCPMedium

Guides cover the methodology, tooling, and strategy that support all of the best practices above. They answer broader questions: How do you measure performance correctly? How do you find which JavaScript is causing the most damage? How do you prevent performance from regressing as the site grows?

Start with Testing for performance and Lab and field data before making any optimizations. Understanding your baseline and measurement tools prevents wasted effort.

GuideSummary
Testing for performanceRun the same Lighthouse benchmark Shopify uses for Theme Store evaluation, set up Lighthouse CI, and interpret Web Performance Dashboard data.
Lab and field dataUnderstand when to use Real User Monitoring (RUM) and when to use synthetic lab tests, and how to use both together for debugging.
Debugging with metric gapsAnalyze the gaps between TTFB, FCP, and LCP in RUM data to narrow down root causes before running lab tests.
Understanding INPA detailed look at Interaction to Next Paint: the three phases of input delay, processing time, and presentation delay, and how to reduce each.
Finding your worst JavaScript offendersUse Lighthouse Treemap, DevTools Coverage, and network analysis to identify the 20 percent of scripts causing 80 percent of problems.
Avoiding fake performance appsRecognize apps that cheat Lighthouse and PageSpeed tests, such as apps that inject transparent elements or serve different pages to crawlers, and avoid them.
Theme Check linting toolUse Shopify's official linter to catch performance violations, such as oversized bundles, parser-blocking scripts, and remote assets, before deployment.
Liquid performance patternsA reference of compounding Liquid micro-optimizations that together can save 25 to 50 ms of TTFB on complex themes.
Build a sustainable performance practiceWhy one-time performance projects fail, how to build a repeatable improvement cycle, and how to get organizational alignment to keep a storefront fast over time.
Performance in HydrogenReact optimization patterns such as code splitting and memoization, image prioritization, and third-party script management for Hydrogen storefronts.

Mobile devices account for the majority of Shopify storefront traffic and are subject to the most challenging performance conditions: slower CPUs, constrained memory, and variable network connections. Core Web Vitals scores are measured on mobile by default, so mobile performance is theme performance.

The recommendations below address the mobile-specific usability issues that directly affect CWV scores and search rankings. A dedicated mobile experience section is planned, so these recommendations are grouped here in the interim.

RecommendationSummaryMetricImpact
Test with real mobile CPU and network throttlingAddress the CPU, memory, and network constraints that make mobile performance harder than desktop. Test with realistic throttling and representative devices.LCPHigh
Remove intrusive interstitialsAvoid full-screen dialogs and overlays that cover main content on page load. They trigger Google ranking penalties and harm Core Web Vitals.LCPHigh
Build responsive layouts that perform wellUse Shopify's responsive image tools, conditional Liquid rendering, and targeted CSS to serve fast pages across all screen sizes.LCPMedium
Increase tap target sizeMake interactive elements at least 48 × 48 px on mobile to reduce mis-taps and the interaction delays that hurt INP.INPMedium
Fix content wider than the viewportEliminate horizontal scrolling by making sure all content, images, and embeds fit within the device viewport width.CLSMedium
Ensure content parity between mobile and desktopVerify that content hidden on mobile with CSS isn't deprioritized by Google's mobile-first indexer.LCPMedium

Shopify's infrastructure provides a foundation of automatic optimizations that apply to every storefront: a global CDN backed by Cloudflare, Brotli and gzip compression, HTTP/3 and TLS 1.3, and automatic image versioning. Understanding what the platform handles automatically helps you avoid duplicating effort and shows which optimizations are already in place before you write any code.

GuideSummary
The Shopify platformAn overview of the CDN, HTTP/3, Brotli compression, and the automatic optimizations Shopify provides to every storefront.
Audit and optimize checkout extensionsIdentify slow checkout UI extensions, remove unused ones, and work with extension developers to reduce checkout LCP and INP (Shopify Plus).


Was this page helpful?