Render a liquid template when the template is a liquid template

Sandip Mane

By Sandip Mane

on August 4, 2020

Shopify's Liquid Templates is a great way for templating in Ruby on Rails applications.

If the template is as simple as this one then there are no issues.

1{% raw %}
2{% if user %}
3  Hello {{ user.name }}
4{% endif %}
5{% endraw %}

However sometimes we have a liquid template which is using another liquid template. Here is an example.

home.liquid
1{% raw %}
2<!DOCTYPE html>
3<html>
4  <head>
5    <style>{% asset 'main.css' %}</style>
6  </head>
7  <body>
8    {% partial 'header' %}
9    <h1>Home Page</h1>
10  </body>
11</html>
12{% endraw %}

In the above case home.liquid is using two other liquid templates main.css and header.liquid.

Let' see what these templates look like.

main.css
1{% raw %}
2* {
3  color: {{ theme.text_color }};
4}
5a {
6  color: {{ theme.link_color }};
7}
8{% endraw %}
header.liquid
1{% raw %}
2<nav>
3{{ organization.name }}
4</nav>
5{% endraw %}

In order to include the assets and the partials we need to create liquid tags.

Let's create a tag which will handle assets.

1
2# app/lib/liquid/tags/asset.rb
3
4module Liquid
5module Tags
6class Asset < Liquid::Tag
7def initialize(tag_name, name, tokens)
8super
9@name = name.strip.remove("'")
10end
11
12      def render(context)
13        new_context = context.environments.first
14        asset = Template.asset.find_by(filename: @name)
15
16        Liquid::Template.parse(asset.content).render(new_context).html_safe
17      end
18    end
19
20end
21end

Let's create a tag that will handle partials.

1
2# app/lib/liquid/tags/partial.rb
3
4module Liquid
5module Tags
6class Partial < Liquid::Tag
7def initialize(tag_name, name, tokens)
8super
9@name = name.strip.remove("'")
10end
11
12      def render(context)
13        new_context = context.environments.first
14
15        # Remember here we are not passing extension
16        asset = Template.partial.find_by(filename: @name + ".liquid")
17
18        Liquid::Template.parse(asset.content).render(new_context).html_safe
19      end
20    end
21
22end
23end

Let's create a new initializer and we need to register these tags in that initializer.

1
2# config/initializers/liquid.rb
3
4require 'liquid/tags/asset'
5require 'liquid/tags/partial'
6
7Liquid::Template.register_tag('asset', Liquid::Tags::Asset)
8Liquid::Template.register_tag('partial', Liquid::Tags::Partial)

Restart the server and now we can render the home.liquid template like this.

1template = Template.template.find_by(filename: "home.liquid")
2
3attributes = {
4organization: {
5name: "Example"
6},
7theme: {
8text_color: "#000000",
9link_color: "#DBDBDB"
10}
11}
12
13Liquid::Template.parse(template.content).render(attributes).html_safe

Here we have a simple implementation of the tags. We can do much more, if needed, like looping over items to parse each item from the partial. That can be done by registering a separate tag for the item and passing in the id of the item so that the specific item can be found and parsed.