In this post, I want to share how to leverage code splitting for your Shopify theme. There is a minor issue to implement it as you would typically do in a Webpack project which I will share a solution for it.
What is code-splitting, and why you may want to do it?
If you don't know what code-splitting is, then, in short, it is a way to deliver the least amount of JS to the browser on page load and let the loaded JS decide when to download the other parts of the code. It makes your website faster to load fists, but the user pays the cost of downloads later in the journey.
How is it done with Webpack?
Basically, all bundling tools are capable of code splitting. You can see a report about it here: https://bundlers.tooling.report/code-splitting/
With Webpack there are multiple ways to achieve code-splitting, for the sake of an example you may split lodash like so:
import('lodash').then(({ default: _ }) => {...})
To read more, start here: https://webpack.js.org/guides/code-splitting/#dynamic-imports
After building, you will get two bundled JS files:
- index.bundle.js
- vendors-node_modules_lodash_lodash_js.bundle.js
You only include index.bundle.js
into your Shopify theme. For example, in the head
of /layouts/theme.liquid
and then the inde.bundle.js will load the other bundles (if needed) afterwards.
<script src="{{ 'index.bundle.js' | asset_url }}" defer></script>
The problem
The problem is Shopify serves your theme assets from a CDN that has a different domain. Also, the URL structure is not what Webpack would figure out by itself. Assuming your shop URL is myshop.com
, you may see your index.bundle.js
is loading from https://cdn.shopify.com/s/files/1/1844/8937/t/273/assets/index.bundle.js
A Solution
Webpack provides an option to define the publicPath on the fly. We can specify the path in the first line of our JS src like this:
// keep this before your imports
__webpack_public_path__ = window.assetsPath;
And the value of the assetsPath
can be assigned in your /layouts/theme.liquid
file.
<script type="application/javascript">
window.assetsPath = "{{ 'index.bundle.js' | asset_url }}".split("index.bundle.js")[0]
</script>
<script src="{{ 'index.bundle.js' | asset_url }}" defer>
And there you have it, code-splitting in your Shopify theme to improve your speed scores and make your client happier.
Cheers!
Top comments (2)
Did you find a way for this to work in the dev theme editor?
I got it to work if you dynamically set the assets path using the global shopify object to detect if you are in the theme editor:
/* eslint-disable */
__webpack_public_path__ = process.env.NODE_ENV === 'production' || Shopify.designMode ? window.assetsPath : '/assets/'
Then you might need to add a different chunk filename output setting in your webpack config so that the shopify CLI detects changes to any modules:
output: {
...
filename: '[name].js',
chunkFilename: '[chunkhash].js',
},