Blog posts are one of the most important traffic driving tools for a store. You can use it to inspire your audience, promote ideas, share beautiful content and engage with your users.
Sometimes, it can also be useful to showcase your products alongside relevant information, so that you can create a direct link between your content and your products. Showcasing accessories that match your own clothing line, suggesting toys for different activities, or even creating seasonal lookbooks for your store. Unfortunately, there’s no out-of-the-box option on Shopify to link your product directly inside your blog post, so we ventured to do exactly that.
Now, we could do it a few different ways. JavaScript is the first thing that popped to mind, but the asynchronous execution between rendering the DOM and running once the document was ready, was not our pick for an ideal solution. No, we wanted to see if we could do that using Liquid alone.
So, here goes.
We’re going to be using a regular loop inside the blog post text (article.content), so that we can identify all the instances where a specific string appears (containing the product handle for the product we want to display).
For this example we are using a bracketed open and close string, [PROD] and [/PROD], in order to identify the product handles and replace those with the actual product information. You can use any string you like, just make sure it’s not repeated inside the text, apart from where you want to inject your product’s data.
Once you’ve gone inside your blog post and inserted the handles for the products you want to display, then you need to edit you article template.
You’ll need to find your {{ article.content }} object and replace it. We start off by assigning the bracketed strings we’re going to be using for splitting up the blog text.
You can use whatever string you prefer, just make sure it’s not repeated elsewhere
{% assign divider = '[PROD]' %}
{% assign dividerClose = '[/PROD]' %}
Next up we need to actually treat our article content as a long string and split it into an array, by using our divider. We’re just assigning a new variable containing the article.content and then we’re transforming it into an array by using the liquid filter ‘split’.
{% assign text = article.content | split: divider %}
We’ve now created an array with as many objects as your products in the blog post, plus one (the first part of the divided text). This will come into play later on because we need to calculate how many times our subsequent loop will run.
The first part of the array is the text that precedes our first product handle. So it should come first, since that’s how our article begins. By indexing the {{ text }} variable, we can get its first part as follows:
{{ text[0] }}
Next up, we need to create a loop for the rest of the text and identify how many products we can find. So this loop will run for as many dividers as it can find inside our text:
{% for divider in text %}
Do something here as many times as the loop will run for.
{% endfor %}
We’ll assign a iteration number (x), so we know where we’re at; we’ll use the forloop.index for that, which identifies the current step of the loop. We’re also assigning a newtext variable which is the leftover text, but turned into an array once more, this time by splitting it using our closing bracket (in this case [/PROD], which we assigned earlier). Therefore:
{% assign x = forloop.index %}
{% assign newtext = text[x] | split: dividerClose %}
From there we can get to the interesting part of finding our product information.
The first part of the new array is our product handle—since we’ve already gotten rid of the opening bracket during our first split and the closing one right above. The productHandle variable can be indexed on 0, since we’ll always need the first part of the remaining text. After that, all we need to do is call up the specific product and render whatever we want in its place.
{% assign productHandle = newtext[0] %}
{% assign product = all_products[productHandle] %}
We can render a new HTML element, just by using the all_products attribute (eg. fetching the product title would be all_products[productHandle].title). We can always render our theme’s default product block if we feel like it. By assigning a product variable, it will carry over to our include command, and render every product element as necessary.
{% include 'product-block' %}
After we’ve rendered our product block in place of our bracketed product handle, we need to display the rest of the text, which is the second part of the array.
{{ newtext[1] }}
The more keen eyed will notice that the discrepancy between the forloop.index and the array indexing can become a problem. In short, if we have 3 products included in our blog post, the loop will run 4 times, which will create an issue of duplicate texts. The solution to compensate for that is surprisingly simple, we just need to override the first loop. We can do that just by offsetting our loop by 1:
{% for divider in text offset:1 %}
<!-- The rest of our code here -->
{% endfor %}
And there you have it. Instead of your [PROD]your-product-handle[/PROD] string inside your blog text, you’ll find your product rendered any way you prefer.
Grab the code here:
{% assign divider = '[PROD]' %}
{% assign dividerClose = '[/PROD]' %}
{% assign text = article.content | split: divider %}
{{ text[0] }}
{% for divider in text offset:1 %}
{% assign x = forloop.index %}
{% assign newtext = text[x] | split: dividerClose %}
{% assign productHandle = newtext[0] %}
{% assign product = all_products[productHandle] %}
<!-- Render your product block here -->
{{ newtext[1] }}
{% endfor %}
–
So, there you go. Now you can get creative with how you insert products in your blog posts, to create a more meaningful experience for your potential customers.