ERB
The ERB module provides a templating system that lets you embed Ruby code within text documents. It’s widely used in Rails views, configuration templates, and any scenario where you need to generate dynamic text from static content mixed with Ruby logic.
Overview
ERB stands for “Embedded Ruby”. It allows you to insert Ruby expressions and statements into plain text files, which are then processed to produce final output with the Ruby code executed:
<%= ... %>— Evaluates the expression and outputs the result<% ... %>— Evaluates the expression without output<%# ... %>— Comments (not included in output)
Loading ERB
require 'erb'
Basic Usage
Simple interpolation
The ERB.new(template) call compiles the template string into an ERB object that can be rendered multiple times. Calling .result(binding) evaluates the embedded Ruby expressions against the current variable scope, so any local variable named name in the surrounding code is accessible inside the <%= %> tag. This makes ERB a natural fit for situations where you already have variables in scope — a view rendering a controller instance variable, for example.
require 'erb'
template = "Hello, <%= name %>!"
result = ERB.new(template).result(binding)
# Assuming name = "World" => "Hello, World!"
The result(binding) approach shown above is convenient when you already have the data in local variables, but it exposes every variable in scope to the template. For tighter control, result_with_hash lets you pass an explicit hash and limits the template’s visibility to only the keys you specify. This is the safer choice when rendering user-supplied templates or when you want to be deliberate about what data flows into the output.
Using a hash
require 'erb'
template = "Welcome to <%= place[:name] %>!"
binding_data = template, { place: { name: "RubyLand" } }
result = ERB.new(template).result_with_hash(place: { name: "RubyLand" })
# => "Welcome to RubyLand!"
Template files
In production code, ERB templates almost always live in separate .erb files rather than inline strings. The pattern is straightforward: read the file, create an ERB instance, call result or result_with_hash to produce the output, and write it somewhere. Rails automates this pipeline for you, but understanding the underlying File.read → ERB.new → .result flow is useful when you need ERB outside of Rails — for config generation, static site building, or email body assembly.
require 'erb'
# Read from file
template = File.read('page.html.erb')
renderer = ERB.new(template)
output = renderer.result(binding)
File.write('page.html', output)
ERB tags
Output tag <%= %>
The equals-sign tag is the most common ERB construct. It evaluates the Ruby expression between <%= and %> and calls .to_s on the result before inserting it into the output stream. Because it automatically converts the return value to a string, you can use any Ruby expression that produces a value — method calls, arithmetic, ternary operators, or even chained enumerable calls.
template = <<~ERB
<h1><%= title %></h1>
<p>You have <%= items.count %> items</p>
ERB
ERB.new(template).result_with_hash(title: "My Page", items: [1,2,3])
# => <h1>My Page</h1>
# => <p>You have 3 items</p>
The output tag handles simple value insertion, but real templates need control flow. The expression tag <% %> runs Ruby code without adding its return value to the output. This is how you write conditionals, loops, and variable assignments inside a template — the tag executes the code, but only the <%= %> tags produce visible output.
Expression tag <% %>
Without the equals sign, code runs but nothing is output:
template = <<~ERB
<% items.each do |item| %>
<li><%= item %></li>
<% end %>
ERB
ERB.new(template).result_with_hash(items: ["apple", "banana"])
# => <li>apple</li>
# => <li>banana</li>
The block form of each inside ERB tags is a pattern you’ll see in nearly every Rails view. The <% items.each do |item| %> line opens the loop, <%= item %> outputs each element, and <% end %> closes it. None of the <% %> tags produce output themselves — only the <%= %> tag does. This separation between logic and output is the core design principle of ERB.
Comment tag <%# %>
Comments are stripped from output:
template = "<%# This is ignored %><%= 1 + 1 %>"
ERB.new(template).result
# => "2"
Comments are stripped entirely during rendering, so they add no bytes to the final output. Use them to annotate complex template logic or to temporarily disable a section without deleting it. Unlike HTML comments (<!-- -->), ERB comments never appear in the rendered source, which makes them safe for hiding implementation details from end users.
Suppressing newlines
Append - to close tags to trim surrounding whitespace:
template = <<~ERB
<% items.each do |item| -%>
<li><%= item %></li>
<% end -%>
ERB
This prevents extra blank lines in the output.
The -%> syntax is especially useful inside loops and conditionals where each iteration would otherwise produce a stray newline in the output. By appending the hyphen, you tell ERB to consume the newline that follows the closing tag, keeping the generated output compact and predictable. The combination of -%> and -%> on both sides of a tag is rarely needed and can make templates harder to read — prefer trimming only the side that actually causes problems.
ERB methods
ERB.new(template, safe_level = nil, trim_mode = nil)
Creates a new ERB template object.
| Parameter | Type | Default | Description |
|---|---|---|---|
template | String | Required | The ERB template string |
safe_level | Integer | nil | Security level (deprecated in Ruby 3.0+) |
trim_mode | String | nil | Trim mode configuration |
The ERB.new constructor accepts the template string as its first argument and two optional parameters for security and whitespace control. The safe_level parameter is deprecated as of Ruby 3.0 and should be left at nil in modern code. The trim_mode string is the one you’ll reach for most often when the default whitespace behavior produces unwanted blank lines in the output.
Trim modes
# Default: no trimming
ERB.new(template, trim_mode: nil)
# Remove leading whitespace before %> (common)
ERB.new(template, trim_mode: '-')
# Remove whitespace around ERB tags
ERB.new(template, trim_mode: '<>')
# Combine: trim before and around tags
ERB.new(template, trim_mode: '-<>')
Common trim mode behaviors:
'-'— Strips whitespace before-%>and newline after'<>'— Strips whitespace between<%and%>tags'-<>'— Combination of both
The three trim modes cover most whitespace-handling needs. The '-' mode is the most commonly used because it solves the “extra blank lines from loops” problem without changing the behavior of <%= %> tags. The '<>' mode is more aggressive — it strips whitespace around all ERB tags — and is better suited for minified or line-sensitive output formats like plain-text email headers.
result(binding = nil)
Renders the template with the given binding:
erb = ERB.new("Value: <%= x * 2 %>")
erb.result(binding) # Uses current binding
erb.result_with_hash(x: 5) # Uses hash as local variables
The result method uses Ruby’s binding object, which captures the entire local variable scope at the call site. This is convenient but broad — every variable in scope becomes accessible to the template. For tighter control over what data reaches the template, prefer result_with_hash, which accepts an explicit hash and exposes only those keys to the ERB rendering context.
result_with_hash(hash)
Renders template with a hash as local variables:
erb = ERB.new("Hello <%= name %>!")
erb.result_with_hash(name: "Alice")
# => "Hello Alice!"
result_with_hash is the safer choice when rendering templates from untrusted callers or when you want to be explicit about which data the template can access. Each hash key becomes a local variable inside the ERB context, so result_with_hash(name: "Alice") makes name available inside <%= name %> tags without exposing anything else from the surrounding scope.
def method(result = nil)
Creates a method from the template:
erb = ERB.new("Hello <%= @name %>!")
erb.def_method(@template, :greeting)
template = ERBTemplate.new
template.@name = "Bob"
template.greeting # => "Hello Bob!"
The def_method approach generates a Ruby method from an ERB template and binds it to a class. This is an advanced feature — it compiles the template once and lets you call it repeatedly with different instance variable state. In practice, most applications use result or result_with_hash for their simplicity, but def_method can be useful when you need to render the same template many times in a tight loop and want to avoid re-parsing it on each iteration.
Security considerations
Safe Levels (Ruby 2.x and earlier)
Older Ruby versions had $SAFE taint checking. This is deprecated as of Ruby 2.7 and removed in Ruby 3.0.
HTML escaping
By default, ERB does not escape HTML. Use CGI.escapeHTML or Rails’ html_escape:
# Manual escaping
template = "<%= CGI.escapeHTML(user_input) %>"
# In Rails (automatic)
template = "<%= user_input %>" # Automatically escaped in Rails
ERB itself does not perform any automatic escaping. If you render <%= user_input %> without wrapping the value in CGI.escapeHTML, anything the user typed — including <script> tags — will appear verbatim in the output. Rails adds automatic HTML escaping to ERB, which is why Rails views are safe by default. When using ERB outside of Rails, always decide whether escaping is needed and apply it explicitly.
Code injection
Never pass untrusted input as template code. Only allow data interpolation:
# DANGEROUS: User controls Ruby code
template = "<% #{user_input} %>" # NEVER DO THIS
# SAFE: User controls only data values
template = "<%= user_input %>" # User input is treated as text
The distinction between <% and <%= is critical for security. The <% tag executes arbitrary Ruby code, so passing user input directly into a <% %> block is equivalent to eval. The <%= %> tag treats its content as a value to convert to a string, which is safe as long as the value itself is not executable code. Never interpolate user-supplied strings into the template source itself — only into the data values that the template references.
Practical examples
HTML generation
require 'erb'
template = <<~ERB
<!DOCTYPE html>
<html>
<head><title><%= title %></title></head>
<body>
<h1><%= heading %></h1>
<ul>
<% items.each do |item| %>
<li><%= item %></li>
<% end %>
</ul>
</body>
</html>
ERB
The HTML template above demonstrates the most common ERB workflow: a static page skeleton with dynamic values injected at specific points. The trim_mode: '-' option strips trailing newlines from <% end %> tags, keeping the generated HTML compact. Without it, the generated HTML would contain gaps where the loop body ends, which browsers ignore but which make the source harder to read and diff. For scenarios where you are generating structured data files instead of web pages — the same ERB pipeline works without modification.
To render the template, call result_with_hash with the values you want to inject into the placeholders. Each hash key becomes a local variable available inside the ERB tags at evaluation time, and the method returns the completed output string with all Ruby expressions resolved and substituted.
output = ERB.new(template, trim_mode: '-').result_with_hash(
title: "My Page",
heading: "Welcome",
items: ["Feature 1", "Feature 2", "Feature 3"]
)
Configuration files
The same ERB pipeline works for generating structured data files — YAML configs, CSV exports, or plain-text reports. Define your template as a heredoc, pass in the configuration values through result_with_hash, and write the output to disk.
require 'erb'
config_template = <<~CONF
database:
host: <%= db[:host] %>
port: <%= db[:port] %>
username: <%= db[:user] %>
password: <%= db[:pass] %>
CONF
config = {
db: {
host: "localhost",
port: 5432,
user: "admin",
pass: "secret"
}
}
File.write("config.yml", ERB.new(config_template).result_with_hash(config))
The configuration-file pattern is one of ERB’s strongest use cases outside of HTML. By writing a YAML template with embedded Ruby, you can generate environment-specific config files at deploy time — injecting database credentials, hostnames, and API keys that differ between staging and production — without maintaining separate copies of the same file.
Email templates
require 'erb'
email_template = <<~EMAIL
From: <%= from %>
To: <%= to %>
Subject: <%= subject %>
Hello <%= name %>,
<%= body %>
Best regards
EMAIL
Email templates benefit from ERB’s ability to interpolate dynamic content — recipient names, dates, and custom messages — into a fixed structure that stays consistent across every send. Using result_with_hash for email rendering is particularly appropriate because email templates should only access the specific fields you intend to expose, not arbitrary variables from a controller or service object.
email = ERB.new(email_template).result_with_hash(
from: "noreply@example.com",
to: "user@example.com",
subject: "Welcome!",
name: "User",
body: "Thank you for signing up."
)
Ruby version history
- Ruby 1.8: Introduced ERB as part of standard library
- Ruby 1.9: Added encoding support for templates
- Ruby 2.7: Deprecated
$SAFEand taint checking - Ruby 3.0: Removed safe level functionality
- Ruby 3.2: Added
result_with_hashmethod