I've been doing Shopify performance optimization for many years and each time I've hit a brick wall—applications. Everything else is fully optimizable on Shopify. You can lazy load images, optimize the theme JavaScript assets load, have total control over your code. Everything is great until you want to optimize apps that are injecting code via the ScriptTag API.
What is wrong with ScriptTag API apps?
This API allows app developers to include JavaScript on the storefront. The JS is added in a Liquid object called content_for_header
and is appended on the storefront after the onload event. The onload event is the only good part as is fired when the whole page has loaded. The really bad part is that all the scripts are loaded on all pages. Even if you just need one app on a product page, you will load it everywhere.
JavaScript, the main performance bottleneck nowadays
We used to optimize the hell out of every image to save a few kilobytes. Network optimization is still important but on most optimization projects I've worked on, JavaScript is the main problem. Scripts have to download, parse, compile and execute. This takes a lot of CPU, especially on mobile devices. Lighthouse will flag when performance metrics like Time To Interactive or Total Blocking Time are above-recommended limits. These metrics are directly connected to the JavaScript execution. While the CPU is busy crunching JavaScript, users may not be able to properly interact with your store, the main (and only) UI thread will be frozen.
App optimization, looking for a solution for ages
The injection happens on a Liquid object called content_for_header
. It is a big chunk of code where Shopify is adding a lot of stuff. A function called asyncLoad()
dynamically loads those app URLs. This is all wrapped in an IIFE, nothing gets in or out.
My initial approach was to do a Liquid string replacement on content_for_header but this can fail miserably and break the whole store.
What we need to optimize ScriptTag API apps
- We need to block the initial script injection
- We need some logic to allow scripts/apps only on pages they have to run
- We need to be able to load apps on user interaction (lazyload, hydration)
The solution!
Since there is no access to Shopify's asyncLoad()
function I could only found one viable solution: hijacking the document.createElement
. JavaScript is extensible, you can "defineProperties
" of createElement and add your custom logic. By overwriting createElement set you can "intercept" the asyncLoad() function dynamic script creation. I've started to laugh by myself after trying this out and it worked.
The section
I've written a section, you can find the code on GitHub. Copy the code to your theme and add the section to your layout.liquid file before the end of the body tag.
There are 3 ways you can use to modify the way ScriptTag APPs work:
- Block the app from loading on a given page. There is no point to load a collection page app on a product page
- Load the app on user scroll. If something is not visible in the initial viewport, you can wait for the user to initiate the scroll event to load the app.
- Load the app on user interaction. Load the app only when an event is triggered, for example, the user clicked on the element with “.open-search-js” class name.
Please see this video for the section setup:https://www.youtube.com/watch?v=UFdTKDPCc_Q
Step by step installation and configuration
- Create a file in your theme section folder: app-optimization.liquid
- Copy the section code from: GitHub and add it to app-optimization.liquid
- Open theme.liquid, add the section before the closing
</body>
tag:{% section 'app-optimization' %}
- Customize the theme, go to the Theme Editor and, select the “App Optimization” section
- For each script in the section panel, create a new section block, click on “Add Application”.
- Copy the app script URL and add it under the “App ScriptTag URL”
- Add an application title. Can be anything. Probably the most challenging part is to match the script URL with the actual app. Some may be harder to figure out.
- You have 3 options to optimize the script: block, load on scroll and, load on interaction. The interaction settings take a comma-separated CSS selectors list and an event type. You can add up to 2 different events.
I hope this helps with your Shopify speed optimization. If you have any suggestions please feel free to contribute to the code on GitHub. Looking forward to hearing your thought. Happy optimization!