Have you felt it? You aren’t hallucinating. Storefronts on Shopify have gotten faster. Not only are the bits getting across the internet to the browser faster, we have added new functionality to enable you to design faster rendering pages as well. Storefronts can now benefit HTTP/3 priorities by utilizing single domain hosting for static content, and sections can now intelligently mark lazy loaded content. Storefronts are faster.
The Data
Over the last twelve months the Chromium User Experience (CrUX) data shows that users to storefronts are getting a faster experience. On average, 35% more sessions are experiencing passing Time to First Byte (TTFB) metric in Core Web Vitals (CWV), and we have expanded our global points of presence. This means that buyers are getting a more consistent experience regardless of where they are — whether they are on a train, in a coffee shop with shared wifi, or on the other side of the planet.
While TTFB isn’t one of the metrics used in SEO, it’s the first bottleneck to getting a great SEO CWV score. Generally, a faster HTML response to the browser, the faster the browser can parse and render the page. This also applies to headless commerce solutions — the faster the Storefront API queries return, the faster the experience to the user. Both headless storefronts and the Online Store are noticeably faster!
As a quick quick refresher, Core Web Vitals (CWV) for SEO focuses on three key metrics:
- How quickly the hero image loads (Largest Contentful Paint),
- How much the screen layout jumps around and confuses buyers (Cumulative Layout Shift),
- And when the user thinks the page is loaded and starts to scroll or touch parts of the page (First Input Delay).
Can I Query This Data Myself?
The nice thing about using CrUX is it is based on real-human experiences. It isn’t based on synthetic lab experiments like Lighthouse. Those kinds of lab tests are great for predicting the user experience, while CrUX reveals how users have actually experienced your store. Even better is the data is public (it’s anonymous and based on a random sampling of Chrome users). For sites you own, you can head to the Google search console to look at your own CWV reports. For the more adventurous, you can run this SQL query of CrUX in BigQuery for your own shop:
SELECT
date,
fast_ttfb -- this is % of sessions with passing CWV TTFB experiences
FROM
`chrome-ux-report.materialized.metrics_summary`
WHERE
date >= '2022-12-01'
AND origin = "https://shoesbycolin.com"
ORDER BY date ASC
What’s Changed?
To achieve this, we have been building more efficiency into the platform so we don’t have to depend on scaling servers to improve speed during high volume periods like Black Friday Cyber Monday (BFCM) or flash sales. Additionally, we’ve been leaning into more distributed designs so that the geographic location of the database doesn’t impede the user experience.
The metrics above are just from the first wave of these changes. In the coming months, we will reveal more details about this work and the bigger changes we have planned. Stay tuned!
In addition to the changes to the platform, we’ve also enhanced the way that browsers render your storefronts to make them feel faster. Two notable changes include:
- Consolidated hosting — static content now shares the same domain as your storefront instead of using
cdn.shopify.com
- Theme Sections and blocks can intelligently apply lazy-loading techniques based on the location on the page
Single Host for Static Content
One change that you might have spotted is that the static content is now hosted on the same hostname as your storefront. When Shopify started, the best practices for browsers was to host static content like images, javascript and css on a separate domain like cdn.shopify.com
. This allowed browsers to multi-plex downloads and speed up rendering.
Using techniques like Early-Hints has helped bridge the experience for browsers by connecting to the static domain earlier and paying the TLS connection tax. While this helps to address the head-of-line blocking of the TLS session, it doesn’t solve the problem of congestion window growth and competing requests across sockets.
Now with HTTP/3 priorities, it is now much more effective to reuse the same hostname for both dynamic and static content. Instead of using cdn.shopify.com
, storefronts now use the prefix /cdn
for the static content. This lets the browser ensure that the critical javascript is loaded quickly before downloading other content that the user might not see just yet (such as the second hero image in the top carousel).
The following synthetic webpagetest waterfall illustrates before and after differences in how the browser discovers and loads resources. Before, the browser must wait for the TLS socket to complete before downloading the CSS and then proceed to the javascript files. After, the browser can initiate the CSS downloading while the HTML is still downloading. This allows the browser to shave off nearly 400ms off the Largest-Contentful-Paint!
How Do I Utilize the Single Domain?
Most of the changes are automatic. Theme code that uses the asset_img_url
, asset_url
or any of the other hosted file filters will automatically point to the shared domain location.
However, if you have any customizations in the liquid code that specifically point to cdn.shopify.com
you can re-write those urls to make them relative urls under /cdn
. Be sure to remove the shop identifiers that prefixed the filename under the previous shared domain. For example:
<!--before-->
<img src="//cdn.shopify.com/s/files/1/0123/4567/8910/files/Blue-Shoes.jpg>
<!--after-->
<img src="/cdn/shop/files/Blue-Shoes.jpg>
Intelligent Lazy Loaded Images
Lazy loading images with <img loading=lazy>
is an important technique to ensure that the browser doesn’t download images in the wrong order. The challenge for theme developers is that a section or block might be placed anywhere on the page by the content team. If the section or block is placed below the fold, this could help make the page load faster, but if the section contains the hero image or product image, then loading=lazy
would make the page feel slower (and also negatively impact Largest-Contentful-Paint).
To help theme developers resolve this, there is a small liquid drop attribute that theme developers can use to infer where the section is rendering on the page. Using section.index > 3
is a good rule of thumb to use as a signal to lazy load. For example:
{% if section.index > 3 %}
<iframe src="video-player.html" loading="lazy"></iframe>
{% else %}
<iframe src="video-player.html"></iframe>
{% endif %}
Can This be Done Automatically?
Yes! Any images that use the image_tag
or the metafield_tag
will automatically consider the section index before generating the loading=lazy attributes. Best of all, this works great with the single domain hosting as it provides a signal to the browser about the prioritization of content.
{{ product | image_tag }}
<!-- output -->
<img src="/cdn/shop/files/Blue-Shoes.jpg" loading="lazy">
More to Come
We know that performance matters for storefronts on the web. We’ve previously shared how improving the first-impression in a web journey increased conversion. An increase in 10% faster first experience, on average, saw an increase of 7% in conversion. This is why we are committed to making the platform faster automatically and enabling more features for you to build your stores so they are faster for all buyers.