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.

Stay up to date with our blogs. Sign up for our newsletter.

We write about Ruby on Rails, ReactJS, React Native, remote work,open source, engineering & design.