Shopify Functional Snippet Composition
So you’ve been asked to do some development work on a Shopify store. Fortunately, most of the elements in the designs you’ve been given look familiar and there are a bunch of snippets available for you to use. Great! This should speed up development. All you need to do is include the snippets in the relevant areas, right? Not so fast. There’s a good chance your snippets won’t work as is, because they were originally written to be used in one or two specific contexts.
So what’s the problem with snippets?
Snippets are essentially a handy way to move one block of code out of a file and into a separate one, which can improve readability. Imagine writing a newspaper article, but each paragraph is a separate file. When the article is published, each file is then combined to create the finished piece. This is great. It means that you can easily reference parts of your article elsewhere.
Unfortunately, that approach to writing snippets limits how useful they can be, since they’re not being passed any contextual information. It would be like writing a snippet to print Article written by John Bailey
. Useful, but only for articles published by John Bailey.
Snippets are actually slightly more context-aware than I’d hinted above. In the example below, the written-by
snippet, will have access to the article author, because snippets have access to the scope of the item they’re being included in, such as an article page. So when the template is built, it knows what article.author
is if the snippet is included in an article.
{% comment %}
written-by.liquid
{% endcomment %}
Article written by {{ article.author }}
The problem with this approach is that the snippet can now only be used within the context of an article. If for example, you wanted to attach it to a novel, the templating engine wouldn’t automatically know to associate article.author
with novel.author
.
How should I approach a snippet then?
Liquid supports passing arguments into snippets, which is perfect for creating components that can be reused in different contexts. To extend the written-by
snippet from before, we would include our snippet into an article like this:
{% comment %}
article.liquid
{% endcomment %}
{% include 'written-by', author: article.author %}
Then simplify the snippet like this:
{% comment %}
written-by.liquid
{% endcomment %}
Article written by {{ author }}
By removing the reference to the article
in the snippet code, we’ve converted the snippet into a component that can be reused in other contexts.
Assigning variables before includes
If you really want to add a few keystrokes, an alternative method to the one described above is to assign generic variables before including the snippet, as shown here:
{% comment %}
article.liquid
{% endcomment %}
{% assign author = article.author %}
{% include 'written-by' %}
You can often get by with this approach, however passing variables as arguments has some extra useful properties.
To begin with, an argument passed to a snippet cannot be reassigned. This has the benefit of making our code safer, because we can be sure that we’re not accidentally reassigning it elsewhere. We can also add default values to our snippets. For example, you might assign a default variable like this:
{% comment %}
written-by.liquid
{% endcomment %}
{% unless author %}
{% assign author = 'Unknown' %}
{% endunless %}
Article written by {{ author }}
An alternative method of assigning a default variable, which looks much cleaner avoids the unless
condition altogether.
{% comment %}
written-by.liquid
{% endcomment %}
{% assign author = 'Unknown' %}
Article written by {{ author }}
You have to be careful with the method above though. Assigning author
within the snippet won’t reassign author
within the snippet scope, but will still reassign it within the page or section scope. You can read more about liquid variable scopes to get a better understanding here: Shopify Variable Scopes.
Code Documentation
When creating your snippets, you might want to go as far as to document them in a format similar to JSDoc, or PHPDoc, such as the following:
{% comment %}
written-by.liquid
{% endcomment %}
{% comment %}
@param {String} author
{% endcomment %}
Article written by {{ author }}
To my knowledge there are no packages like this for Liquid, but if you really wanted to, you could make a package to provide autocompletions in your text editor.
At the very least, passing arguments into your snippet directly makes it very clear to anybody reading your code that those variables are there to be used within the snippet.
Should snippets always take arguments?
I’d suggest that you construct your snippets in this way as often as possible, however there are times you might just want to clean up your code, as opposed to making something that is reusable. For example, you might have a js-includes
snippet, for any and all Javascript files being imported onto your layout file. This doesn’t necessarily need to accept any arguments as typically you will only be including javascript in one place on a page, so the likelihood that you need to reuse it is lower.