Render a liquid template when the template is a liquid template

Sandip Mane avatar

Sandip Mane

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.

{% raw %}
{% if user %}
  Hello {{ user.name }}
{% endif %}
{% endraw %}

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

home.liquid
{% raw %}
<!DOCTYPE html>
<html>
  <head>
    <style>{% asset 'main.css' %}</style>
  </head>
  <body>
    {% partial 'header' %}
    <h1>Home Page</h1>
  </body>
</html>
{% 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
{% raw %}
* {
  color: {{ theme.text_color }};
}
a {
  color: {{ theme.link_color }};
}
{% endraw %}
header.liquid
{% raw %}
<nav>
{{ organization.name }}
</nav>
{% 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.


# app/lib/liquid/tags/asset.rb

module Liquid
module Tags
class Asset < Liquid::Tag
def initialize(tag_name, name, tokens)
super
@name = name.strip.remove("'")
end

      def render(context)
        new_context = context.environments.first
        asset = Template.asset.find_by(filename: @name)

        Liquid::Template.parse(asset.content).render(new_context).html_safe
      end
    end

end
end

Let's create a tag that will handle partials.


# app/lib/liquid/tags/partial.rb

module Liquid
module Tags
class Partial < Liquid::Tag
def initialize(tag_name, name, tokens)
super
@name = name.strip.remove("'")
end

      def render(context)
        new_context = context.environments.first

        # Remember here we are not passing extension
        asset = Template.partial.find_by(filename: @name + ".liquid")

        Liquid::Template.parse(asset.content).render(new_context).html_safe
      end
    end

end
end

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


# config/initializers/liquid.rb

require 'liquid/tags/asset'
require 'liquid/tags/partial'

Liquid::Template.register_tag('asset', Liquid::Tags::Asset)
Liquid::Template.register_tag('partial', Liquid::Tags::Partial)

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

template = Template.template.find_by(filename: "home.liquid")

attributes = {
organization: {
name: "Example"
},
theme: {
text_color: "#000000",
link_color: "#DBDBDB"
}
}

Liquid::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.

Follow @bigbinary on X. Check out our full blog archive.