ERB and Twig Cross-Reference for Front-End Development

Henry Bley-Vroman

Posted

A version of this post appeared on viget.com

Two entire flowering plants, a yellow crocus (Crocus species) and a herb Paris (Paris quadrifolia). Colour nature print by A. Auer, c. 1853. Wellcome Collection. Public Domain Mark.

Twig and ERB are the two front-end templating languages I use most when developing websites. Here I document the ways each write just about everything to build views: comments, conditionals, variables and undefined variables, interpolation, loops and the loop index, slicing, handling whitespace, retrieving an keyed values, and templating with blocks and partials. If you're familiar with one of Twig or ERB, use this as a cross-reference to translate your knowledge of the one language into the other. If you haven't used either, this should get you up and running quickly. Read on to learn more about ERB and Twig, or skip ahead to the reference section.

Read more

This post is geared towards developers who want to translate their Twig knowledge to ERB, or vice versa. If you're only interested in one language or the other, check out Fundamental ERB for Front-End Development or Fundamental Twig for Front-End Development.

What is Twig?

Twig is SensioLabs' Django- / Jinja-like templating language for PHP. The recommended extension for Twig files is .twig, .<compiled_extension>.twig is useful, and .html —though inaccurate— is common in front-end templating. It's used by SensioLabs' Symfony; by Drupal 8, which is built on Symfony; and by Craft.

Twig is a great language for building web front ends: it is full-featured without having more than one person could hope to learn, it reads fairly closely to English, and it has great official documentation. Twig is especially notable for its powerful support for complex inheritance across templates. Check out the use tag, the embed tag, and the block() function.

Twig even has Javascript implementations, making it easy to fit into projects built on the JS ecosystem. A quick overview to help you pick the one that best suits yours needs:

  • Mozilla's Nunjucks is officially "jinja2 inspired" but it has [often followed Twig's lead](https://github.com/mozilla/nunjucks/issues?utf8=✓&q=is%3Aissue is%3Aclosed twig ) and is now close enough to Twig that Blendid, Viget's build tool for painless local development of static, Craft, Drupal, or Rails sites uses it as Twig proxy (Nunjucks notably does not support Twig's horizontal embed inheritence). If you use Gulp in your build tools, you can use gulp-nunjucks.
  • Twig.js is a popular JS port of Twig that sees more active development than Nunjucks does. It does not reach full parity with Twig (as of this writing Twig.js notably still has some bugs with Twig's embed tag) but it currently comes closer than Nunjucks does and, since its goal is to duplicate Twig, it likely always will. The Twig.js Gulp plugin is gulp-twig.
  • Twing is a Twig engine for Node.js written in TypeScript which aims to always maintain complete parity with Twig. It is described as "a maintainability-first engine that passes 100% of the TwigPHP integration tests, is as close as possible to its code structure and expose an as-close-as-possible API." Because Twing is able to essentially reuse much of Twig's codebase, adding new features as they are merged into Twig is straightforward. Twing is the youngest of these projects… Twig users, show it your love! gulp-twing lets you use Twing with Gulp.

To learn Twig, read through the official documentation, and try things out in twigfiddle.

What is ERB?

ERB (Embedded Ruby) is a feature of Ruby that lets you —you guessed it!— embed Ruby in other files. ERB files have the extension .<compiled_extension>.erb. It is the language HAML and Slim are shorthand for. ERB is commonly used for templating Views in Rails apps — at Viget we use it when building large sites with custom CMSes. (If that's something you do, check out Colonel Kurtz, the block editor we often use for the client-facing admin area of Rails app sites.)

Because it can do anything Ruby can do, it's extremely powerful, has a much steeper learning curve than Twig, and can do a lot that isn't relevant to front-end templating. There's no cannonical ERB-for-front-end-developers documentation, and the Rails official documentation is immense and hard to dig through. Some resources if for learning ERB:

Reference

Delimiters

Comments

Inline comments

  • ERB: <%# … %>

    erb
    <%# comment %>
    Copied
  • Twig: {# … #}

    twig
    {# comment #}
    Copied

Block comments

  • ERB: =begin=end

    the opening and closing tags must be at the start of the line

    erb
    <%
    =begin %>
    block comment
    (both lines of both the begin and end tags must be at the start of their lines)
    <%
    =end %>
    Copied

    not

    erb
    <%
    =begin %>
    not a comment
    <%
    =end %>
    Copied
  • Twig: {# … #}

    twig
    {#
    block comment
    #}
    Copied

    or

    twig
    not a comment {# block
    comment #} not a comment
    Copied

Outputting values

  • ERB: <%= … %>

    erb
    <%= "print this" %> <%# output: `"print this"` %>
    <%= 1 + 2 %> <%# output: `3` %>
    Copied
  • Twig: {{ }}

    twig
    {{ "print this" }} {# output: `print this` #}
    {{ 1 + 2 }} {# output: `3` #}
    Copied

Execution (Control Code)

  • ERB: <% … %>

    erb
    <% ifdo %> … <% end %>
    Copied
  • Twig: {% … %}

    twig
    {% if … %} … {% endif %}
    Copied

Conditionals

Single-statement conditionals

  • ERB: if and unless

    erb
    <%= 2 if true %> <%# output: `2` %>
    <%= 2 if false %> <%# output: `nil` %>
    <%= 2 unless true %> <%# output: `nil` %>
    <%= 2 unless false %> <%# output: `2` %>
    Copied

Multi-statement conditionals

  • ERB: ifelsifend

    erb
    <%# assuming x, y, z, and n are defined %>
    <% if x %>
    y
    <% elsif z == n %> <%# note the spelling of elsif %>
    0
    <% else %>
    1
    <% end %>
    Copied
  • Twig: ifelseifendif

    twig
    {% if x %}
    y
    {% elseif z == n %}{# note the spelling of elseif #}
    0
    {% else %}
    1
    {% endif %}
    Copied

Conditionals with logical operators

Both ERB and Twig support "condition ? iftrue : iffalse", and "ifselftrue ?: otherwise".

  • ERB. Note that the "then" case : must be provided

    erb
    <%# assuming x, y, z, and n are defined %>
    <%# if x then y %>
    <%# omitting the "else" will throw an error #>
    <%= x ? y : '' %>
    <%# if x is true, y. otherwise, if z equals n then 0. otherwise 1 %>
    <%= x ? y : z == n ? 0 : 1 %>
    <%# ternary operator: x if x is true, otherwise y %>
    <%= x ?: y %>
    Copied
  • Twig

    twig
    {# assuming x, y, z, and n are defined and/or Twig's strict variables option is turned off #}
    {# if x then y #}
    {{ x ? y }}
    {# if x is true, y. otherwise, if z equals n then 0. otherwise 1 #}
    {{ x ? y : z == n ? 0 : 1 }}
    {# ternary operator: x if x is true, otherwise y #}
    {{ x ?: y }}
    Copied

Truth and falsity of zero in Boolean contexts

  • ERB: 0 is True in Boolean contexts

    erb
    <%= false ? 'truthy' : 'falsy' %> <%# output: `"falsy"` %>
    <%= 0 ? 'truthy' : 'falsy' %> <%# output: `"truthy"` %>
    Copied
  • Twig: as in PHP generally, 0 is False in Boolean contexts

    twig
    {{ false ? 'truthy' : 'falsy' }} {# output: `falsy` #}
    {{ 0 ? 'truthy' : 'falsy' }} {# output: `falsy` #}
    Copied

Defining variables

  • ERB: =

    erb
    <% var = 1 %>
    <% anotherVar = 0 %>
    <% falseVar = false %>
    <%= 2 if var %> <%# output: `2` %>
    <%= 2 if anotherVar %> <%# output: `2` %>
    <%= 2 if falseVar %> <%# output: `` %>
    <%= 2 unless falseVar %> <%# output: `2` %>
    Copied
  • Twig: set

    twig
    {% set var = 1 %}
    {% set anotherVar = 0 %}
    {% set falseVar = false %}
    {{ var ? 2 }} {# output: `2` #}
    {{ anotherVar ? 2 }} {# output: null - Twig, unlike PHP, equates 0 with falsehood #}
    {{ falseVar ? '' : 2 }} {# output `2` #}
    Copied

    Twig can define multiple variables in a single call — just keep in mind that developers not used to this might overlook the multiple declarations!

    twig
    {% set x, y, z = 1, 2, 3 %}
    Copied

    (A value must be explicitly provided for each variable: {% set x, y = 1 %} will error.)

Line breaks within a variable's value

  • ERB: multi-line blocks of markup can stored in an identifier with content_for x doend

    erb
    <% content_for longVar do %>
    <div>
    </div>
    <% end %>
    <%= content_for(longVar) %>
    Copied

    Note: content_for is additive: each time you provide content for a given variable, that content is appeneded to what was there already. To use content_for to overwrite a global variable, use the flush: true option:

    erb
    <% content_for refreshedVar do %>
    a
    <% end %>
    <% content_for refreshedVar, flush: true do %>
    b
    <% end %>
    Copied
  • Twig: use the set tag's form set xendset to capture chunks of text

    twig
    {% set longVar %}
    <div>
    </div>
    {% endset %}
    {{ longVar }}
    Copied

Dealing with undefined variables

  • ERB:

    • defined?()

      erb
      <%# output: the content if `var` is defined %>
      <% if defined?(var) %>
      <% end %>
      <%# output: `var` if `var` is defined, otherwise `fallback` %>
      <%= defined?(var) ? var : fallback %>
      Copied
    • ||=, the OR Equal operator

      erb
      <%# output: `var` if it is defined and not nil and not false, otherwise `fallback` %>
      <% var ||= fallback %>
      <%
      =begin %> common front-end use cases:
      1. output a variable only if it is defined
      <%
      =end %>
      <% var ||= nil %>
      <%# set a variable with a fallback %>
      <% x = y ||= nil %>
      Copied
  • Twig:

    • is defined

      Especially useful when Twig's strict variables option is turned on, in which case referring to an undefined variable will throw an error.

      twig
      {# output: Twig_Error_Runtime: Variable "x" does not exist. #}
      {{ x }}
      {# output: the content if var is defined #}
      {% if var is defined %}
      {% endif %}
      {# output: `advance` if var is defined, otherwise `fallback` #}
      {{ var is defined ? advance : fallback }}
      Copied
    • ??, the null coalescing operator

      twig
      {# output: `var` if it is defined and not null, otherwise `fallback` #}
      {{ var ?? fallback }}
      {# common use cases:
      1. output a variable only if it is defined #}
      {{ var ?? null }}
      {# set a variable with a fallback #}
      {% set x = y ?? null %}
      Copied

Variable interpolation

  • ERB: #{var}

    erb
    <% x = 1 %>
    <%= "this is interpolated: #{x}" %><%# output: `this is interpolated: 1` %>
    Copied
  • Twig: #{var}

    twig
    {% set x = 1 %}
    {{ "this is interpolated #{x}" }}{# output: `this is interpolated: 1` #}
    Copied

Concatenation

  • ERB: + (plus). Note that to concatenate a string and a number in Ruby, the number must be converted to a string.

    erb
    <% string_variable = 'world' %>
    <% number_variable = 2 %>
    <%= 'hello ' + string_variable %> <%# output: `"hello world"` %>
    <%= "example #{number_variable}" %> <%# output: `"example 2"` %>
    <%= 'example ' + 3.to_s %> <%# output: `"example 3"` %>
    Copied
  • Twig: ~ (tilde). Note that strings and numbers can be freely concatenated.

    twig
    {% set string_variable = 'world' %}
    {% set number_variable = 2 %}
    {{ 'hello ' ~ string_variable }} {# output: `hello world` #}
    {{ "example #{number_variable}" }} {# output: `example 2` #}
    {{ 'example ' ~ 3 }} {# output: `example 3` #}
    Copied

Iteration (loops)

Iterating over items

  • ERB: n.each do |i|end

    erb
    <% items = ['a', 'b', 'c'] %>
    <%# output: `...` %>
    <% [0..items.length].each do %>.<% end %>
    <%# output: `a b c ` %>
    <% items.each do |item| %>
    <%= item %>
    <% end %>
    Copied
  • Twig: for i in nendfor

    twig
    {% set items = ['a','b','c'] %}
    {# output: `...` #}
    {% for i in 0..items.length %}.{% endfor %}
    {# output: `a b c ` #}
    {% for item in items %}
    {{item}}
    {% endfor %}
    Copied

Using the loop index, 0-indexed

  • ERB:

    • n.each_with_index do |i, index|end

      erb
      <%# output: `0. a 1. b 2. c ` %>
      <% items = ['a', 'b', 'c'] %>
      <% items.each_with_index do |item,index| %>
      <%= index %>. <%= item %>
      <% end %>
      Copied
    • n.times do |i|end

      erb
      <%# output: `0 1 2 3 4 5 6 7 8 9` %>
      <% 10.times do |i| %><%= i %> <% end %>
      Copied
  • Twig: loop.index0

    twig
    {% for item in items %}
    {{loop.index0}}. {{item}}
    {% endfor %}
    Copied

Using the loop index, 1-indexed

  • ERB:

    • .each_with_index's index is always 0-indexed, so add 1

      erb
      <% items.each_with_index do |item,index| %>
      <%= index + 1 %>. <%= item %>
      <% end %>
      Copied
    • n.times do |i|end

      erb
      <%# output: `1 2 3 4 5 6 7 8 9 10 ` %>
      <% 10.times do |i| %><%= i %> <% end %>
      Copied
  • Twig: loop.index

    twig
    {% for item in items %}
    {{loop.index}}. {{item}}
    {% endfor %}
    Copied

Iterating a certain number of times

  • ERB: n.times do |i|end

    erb
    <% n = 3 %>
    <%# output: `...` %>
    <% n.times do %>.<% end %>
    <%# output: `1 2 3 ` %>
    <% n.times do |i| %>
    <%= i %>
    <% end %>
    Copied
  • Twig: for i in nendfor

    twig
    {% set items = ['a','b','c'] %}
    {# output: `...` #}
    {% for i in 0..items.length %}.{% endfor %}
    {# output: `a b c ` #}
    {% for item in items %}
    {{item}}
    {% endfor %}
    Copied

Inspecting data

  • ERB: several options for formatting an object's data, notably: simply outputting, .inspecting, and debug()ing. For basic data-checking purposes in a view, the essential difference is debug() returns YAML while inspect and printing return strings.

    erb
    <%# for some object `posts` %>
    <%= posts %>
    <%= posts.inspect %>
    <%= debug(posts) %>
    Copied
  • Twig:

    • The |json_encode() filter formats an object's data.

      twig
      {# for some object `posts` #}
      {{ posts|json_encode }}
      Copied
    • The dump() function outputs information about a variable.

      twig
      {# for some object `posts` #}
      {{ dump(posts) }}
      Copied

      Note: dump must be enabled. Some implementations make it available out of the box (for example, Craft in dev mode).

Slicing

  • ERB: .slice(index), .slice(start,count)

    erb
    <%= [1,2,3,4].slice(1) %> <%# output: `2` %>
    <%= [1,2,3,4].slice(1,2) %> <%# output: `[2,3]` %>
    Copied
  • Twig: |slice(start,count) or [start:count]

    twig
    {{ [1,2,3,4]|slice(1) }} {# output: `Array` #}
    {{ [1,2,3,4]|slice(1,2) }} {# output: `Array` #}
    Copied

    Note: The output of the above Twig examples is Array, because in Twig the output of {{ [anArray] }} is Array. If you need to print an array, use |json_encode:

    twig
    {{ [1,2,3,4]|slice(1)|json_encode() }} {# output: `[2,3,4]` #}
    {{ [1,2,3,4]|slice(1,2)|json_encode() }} {# output: `[2,3]` #}
    Copied

    In execution, no special steps are necessary:

    twig
    {% set myArray = [1,2,3,4] %}
    Copied

Shorthand to slice the first count items

  • ERB: .take(count) or .first(count)

    erb
    <%= [1,2,3,4].take(2) %> <%# output: `[1,2]` %>
    <%= [1,2,3,4].first(2) %> <%# output: `[1,2]` %>
    Copied
  • Twig: [:count]

    twig
    {{ [1,2,3,4][:2]|json_encode() }} {# output: `[1,2]` #}
    Copied

Shorthand for everything after the start item

  • Twig: [start:]

    twig
    {{ [1,2,3,4][2:]|json_encode() }} {# output: `[3,4]` #}
    Copied

Trimming whitespace

  • ERB

    If trim_mode is set to -, a - in the closing erb tag will trim trailing whitespace:

    erb
    <% something -%>
    1
    <%= something_else -%>
    2
    <% another_thing %>
    Copied

    is equivalent to

    erb
    <% something %>1
    <%= something_else %>2
    <% another_thing %>
    Copied
  • Twig

    Trim leading or trailing whitespace by adding a - inside in an opening or close delimiter, respectively:

    twig
    {% something -%}
    1
    {%- something_else -%}
    2
    {%- last_thing %}
    Copied

    is equivalent to

    twig
    {% something %}1{% something_else %}2{% last_thing %}
    Copied

Trimming space between HTML elements

  • Twig

    Twig doesn't care what language you are compiling to, but it does provide a special spaceless tag for use with HTML.

    twig
    {% spaceless %}
    <div>…</div>
    <span>…</span>
    {% endspaceless %}
    Copied

    is equivalent to

    twig
    <div>…</div><span>…</span>
    Copied

    Note that this spaceless has limited powers:

    • it isn't recursive

      twig
      {% spaceless %}
      <div>
      <div>
      </div>
      <div>
      <span>…</span>
      {% endspaceless %}
      Copied

      is equivalent to

      twig
      <div><div>
      </div><div><span>…</span>
      Copied
    • and content between HTML tags will disrupt it

      twig
      {% spaceless %}
      <div>…</div>
      sorry, spaceless
      <span>…</span>
      {% endspaceless %}
      Copied

      is equivalent to

      twig
      <div>…</div>
      sorry, spaceless
      <span>…</span>
      Copied

Keyed values

  • ERB:

    Use a Symbol :property to look up an operation on a Hash:

    erb
    <% myHash = {hello: 'world'} %>
    <%= myHash[:hello] %> <%# output: "world" %>
    Copied
  • Twig:

    Use dot notation or subscript syntax to access attributes of a variable:

    twig
    {% set myVar = {hello: 'world'} %}
    {{ myVar.hello }} {# output: world #}
    {{ myVar['hello'] }} {# output: world #}
    Copied

Vertical inheritance

For a layout file that pulls in page:

  • ERB: content_for in child, yield in parent

    layouts/layout.html.erb

    erb
    <%= yield :myBlock %>
    Copied

    views/page.html.erb

    erb
    <% content_for :myBlock do %>
    the content
    <% end %>
    Copied
  • Twig: block + extends in child, block in parent.

    layout.html.twig

    twig
    {% block myBlock '' %}
    {# or #}
    {% block myBlock %}{% endblock %}
    {# or #}
    {% block myBlock %}{% endblock myBlock %}
    Copied

    page.html.twig

    twig
    {% extends 'layout.html.twig' %}
    {% block myBlock %}
    the content
    {% endblock %}
    Copied

    or if all the content is a variable x, page.html.twig

    twig
    {% extends 'layout.html.twig' %}
    {% block myBlock x %}
    Copied

    or if all the content is a single string, page.html.twig

    twig
    {% extends 'layout.html.twig' %}
    {% block myBlock "#{x} content" %}
    {# or #}
    {% extends 'layout.html.twig' %}
    {% block myBlock x ~ "content" %}
    Copied

    or if all the content is a single literal string, page.html.twig

    twig
    {% extends 'layout.html.twig' %}
    {% block myBlock 'the content' %}
    {# or #}
    {% block myBlock "the content" %}
    Copied

Vertical inheritance with default content in the parent

  • ERB

    layouts/layout.html.erb

    erb
    <% if content_for?(:my_content) %>
    <%= yield :my_content %>
    <% else %>
    default content
    <% end %>
    Copied

    views/page.html.erb

    erb
    <% content_for :my_content do %>
    the content
    <% end %>
    Copied
  • Twig

    main.html.twig

    twig
    {% block content %}
    default content
    {% block sub_content '' %}
    {% endblock %}
    Copied

    override-content.html.twig

    twig
    {% extends 'main.html.twig' %}
    {% block content %}
    the content
    {% endblock %}
    Copied

    Result of override-content.html.twig:

    default content
    Copied

    override-subcontent.html.twig

    twig
    {% extends 'main.html.twig' %}
    {% block subcontent %}
    the sub-content
    {% endblock %}
    Copied

    Result of override-subcontent.html.twig:

    default content the sub-content
    Copied

Using partials

  • ERB:

    render will output the contents of another file

    erb
    <%= render 'path/to/x' %>
    Copied

    To pass values to the rendered file, define them:

    erb
    <% a = 1 %>
    <% b = 2 %>
    <%= render 'path/to/x', a:a, b:b %> <%# in path/to/x a=1 and b=2 %>
    Copied

    If the rendered file expects different variable names, use those:

    erb
    <% a = 1 %>
    <% b = 2 %>
    <%= render 'path/to/x', y:a, z:b %> <%# in path/to/x y=1 and z=2 %>
    Copied
  • Twig:

    • include tag

      twig
      {% include 'path/to/x' %}
      Copied
    • include function

      twig
      {{ include('path/to/x') }}
      Copied

    The include tag passes the entire parent context to the included file by default:

    twig
    {% set a = 1 %}
    {% set b = 2 %}
    {% include 'path/to/x' %} {# in path/to/x a=1 and b=2 #}
    Copied

    To pass only certain data, use include with only:

    twig
    {% set a = 1 %}
    {% set b = 2 %}
    {% include 'path/to/x' with {a:a} only %}
    {# in path/to/x a=1 and b does not exist #}
    Copied

    Rename variables in the with (can be combined with only):

    twig
    {% set a = 1 %}
    {% include 'path/to/x' with {y:a} %} {# in path/to/x a=1 and y=1 #}
    {% include 'path/to/z' with {y:a} only %}
    {# in path/to/z y=1 and a does not exist #}
    Copied

Related Posts

Next
Previous
Set Up Simulator and Test iOS Mobile Safari on Mac

See All Posts