Skip to content
Orvenant
Orvenant

RUBY · DEVELOPER GUIDE

Ruby multiline strings: heredocs, newlines and clean formatting

Write Ruby multiline strings without a chain of + operators. Choose the right heredoc, control indentation and newlines, and check the exact output with runnable examples.

The short answer: use a heredoc

Yes. Ruby can hold several lines in one string literal. You do not need a chain of + operators.

For a block of text that should keep its line breaks, start with a squiggly heredoc: <<~TEXT. If you only want to wrap a long line of source code, use adjacent quoted literals with a line-continuation backslash. These solve different problems, so decide what the finished string should contain before choosing the syntax.

A complete multiline stringRuby
message = <<~TEXT
  Your deployment is ready.
  Open the project dashboard to review it.
TEXT

p message
Result: A complete multiline stringOutput
"Your deployment is ready.\nOpen the project dashboard to review it.\n"

The \n characters in this output represent real line breaks. p displays the string with escapes, which makes invisible whitespace easy to see. puts would print the two sentences on separate lines. The final newline is part of the value.

The examples in this guide were executed with Ruby 3.4.5. They use core Ruby unless a section explicitly mentions Rails.

First, choose the output you actually need

A string can span several lines in your editor and still produce a single line at runtime. It can also contain newlines even when its source fits on one line.

Which Ruby string syntax should you use?
MethodBest useWhitespace and interpolation
<<~TEXTIndented blocks of readable textCommon indentation removed; newlines kept; interpolation enabled
<<~'TEXT'Literal text containing #{...} or backslashesCommon indentation removed; interpolation disabled
<<-TEXTKeep leading spaces but indent the closing markerContent indentation kept; interpolation enabled
Adjacent literals with \Wrap a long source line without output newlinesInclude word spaces yourself; interpolation follows quote type
%q{...} / %Q{...}Text containing many quote charactersLine breaks kept; %q is literal, %Q interpolates
Ordinary quoted stringShort text with a few linesPhysical line breaks become part of the string

For an email body, preserved line breaks usually make sense. For a log label or a sentence shown beside a button, they may not. For a configuration file, leading spaces may carry meaning. Choosing around the output keeps formatting fixes from quietly changing the data.

Write readable blocks with <<~

A heredoc gives a longer string its own space in the source file. A descriptive closing marker makes the boundary easy to scan.

Keep relative indentationRuby
def deployment_note
  <<~NOTE
    Release checklist:
      Verify the health endpoint.
      Review the error log.
  NOTE
end

p deployment_note
Result: Keep relative indentationOutput
"Release checklist:\n  Verify the health endpoint.\n  Review the error log.\n"

Both checklist items still have two leading spaces. This matters when your text includes a nested list or a deliberately indented section. In code review, read the least-indented nonblank line first: it gives you a useful visual reference for the shared indentation that will disappear.

Choose a marker such as EMAIL_BODY, QUERY or RELEASE_NOTE rather than a generic label in every case. The name does not affect the output; it helps the next developer understand the text. Use a marker that will not also appear alone inside the body.

Understand <<, <<- and <<~

The three heredoc forms differ in how they treat indentation. A misplaced closing marker can also make otherwise simple code fail to parse.

An indented terminator does not remove body spacesRuby
text = <<-TEXT
  Keep these two leading spaces.
  TEXT

p text
Result: An indented terminator does not remove body spacesOutput
"  Keep these two leading spaces.\n"

Use plain <<TEXT when the terminator belongs at the beginning of a line. <<-TEXT lets you indent that terminator. <<~TEXT also handles shared body indentation. Keep the closing marker on its own line, spell it exactly as you opened it, and avoid trailing spaces after it.

If Ruby reports an unexpected end of file after you add a heredoc, inspect the closing marker before rewriting the surrounding method. A typo, an accidental suffix or a marker that has been swallowed by the body is often easier to fix than the error location suggests.

Choose interpolation deliberately

A name, a date or a small calculated value can be inserted with #{...}. Keep larger transformations outside the string so the text remains easy to review.

Insert a value into a heredocRuby
project = "Client portal"
message = <<~EMAIL
  Project: #{project}
  Next step: review the release notes.
EMAIL

p message
Result: Insert a value into a heredocOutput
"Project: Client portal\nNext step: review the release notes.\n"
Keep interpolation syntax as literal textRuby
text = <<~'TEXT'
  The template contains #{project}.
  The characters \n stay visible.
TEXT

p text
Result: Keep interpolation syntax as literal textOutput
"The template contains \#{project}.\nThe characters \\n stay visible.\n"

The second form is useful for documenting a template that contains Ruby interpolation syntax. It avoids looking up project at all. A reviewer can see that the text is intended to be copied as written, rather than assembled from runtime values.

Interpolation is formatting, not validation. When a string is later used as SQL, HTML or a shell command, the rules of that destination still apply. Keep database values in the adapter’s bound parameters; escape output for its HTML context; pass command arguments separately rather than assembling a shell command from user input.

Wrap the source without adding output newlines

For a long sentence that must stay on one line, adjacent literals are often clearer than making a multiline block and cleaning it afterwards.

A long source line, one output lineRuby
message = "Your project is ready for review. " \
          "Open the dashboard when you have a moment."

p message
Result: A long source line, one output lineOutput
"Your project is ready for review. Open the dashboard when you have a moment."

The space after the first sentence is inside the quotes. Without it, the result would join the full stop directly to Open. Keep the backslash as the final character on its source line; do not put a comment after it.

There is no explicit + in this example, although Ruby still combines the adjacent literals. This is useful for fixed text. It is not a replacement for joining an array of values that you only know at runtime.

Join a runtime collection explicitlyRuby
steps = ["Design", "Build", "Review"]
p steps.join(" / ")
Result: Join a runtime collection explicitlyOutput
"Design / Build / Review"

Ordinary quotes and percent strings also work

You do not have to use a heredoc for every short block. Choose the form that leaves the text easiest to read.

A quoted string can contain physical line breaksRuby
text = "Build complete.
Review pending."

p text
Result: A quoted string can contain physical line breaksOutput
"Build complete.\nReview pending."
Percent strings reduce quote escapingRuby
project = "Portal"
literal = %q{The label is "#{project}".}
interpolated = %Q{The label is "#{project}".}

p literal
p interpolated
Result: Percent strings reduce quote escapingOutput
"The label is \"\#{project}\"."
"The label is \"Portal\"."

Here, %q keeps the interpolation expression visible, while %Q inserts the value. The braces are delimiters, not part of the finished text. Percent strings are useful when the body contains many quote marks; they do not automatically remove newlines or tidy indentation.

Avoid switching syntax just to save two escaped quotes if the result is harder for your team to read. Consistency around a small set of patterns is usually more useful than demonstrating every form Ruby supports.

Remove only the whitespace you mean to remove

A cleanup method should express a specific requirement. Removing the final newline and trimming all outer whitespace are different operations.

Remove the heredoc’s final newline with chompRuby
message = <<~TEXT.chomp
  Build complete.
  Review pending.
TEXT

p message
Result: Remove the heredoc’s final newline with chompOutput
"Build complete.\nReview pending."
Compare chomp and strip on intentional whitespaceRuby
text = "  keep my indentation  \n"
p text.chomp
p text.strip
Result: Compare chomp and strip on intentional whitespaceOutput
"  keep my indentation  "
"keep my indentation"

For a comparison in a test, first write the exact string you expect. If the value differs only at the end, inspect the final character rather than adding .strip everywhere. Overly broad cleanup can hide a formatting mistake in one place and remove meaningful spaces in another.

Ruby String documentation: chomp and strip

Rails squish is a separate choice

Some answers about Ruby recommend squish. That method belongs to Active Support, so a standalone Ruby process does not get it simply because it supports strings.

In a Rails application, String#squish can be convenient for a sentence whose whitespace should collapse to single spaces. It also removes whitespace at the beginning and end. Use it for that intended transformation, not as a general repair for a heredoc.

Outside Rails, you would need the Active Support dependency and its string filters extension. Do not add a framework dependency solely to wrap a fixed sentence: the adjacent-literal example above already solves that case in core Ruby.

Rails API: String#squish

Why collapsing all whitespace can change dataRuby
text = "Account: North  America\nStatus: ready"
p text.gsub(/\s+/, " ")
Result: Why collapsing all whitespace can change dataOutput
"Account: North America Status: ready"

The double space in the region name and the line separating the fields have both disappeared. That may be fine for a display label, but it is a change in content. SQL comments, quoted values and whitespace-sensitive formats deserve particular care. Test the finished value against the format you are producing.

Common mistakes worth catching in review

Most bugs here come from invisible characters or from confusing two different uses of the same punctuation.

  • Missing spaces between adjacent literals: inspect the boundary between the two pieces.
  • Assuming <<- removes indentation: use <<~ when shared indentation should be removed.
  • Using .strip when only the final newline is unwanted: check whether .chomp expresses the requirement.
  • Expecting %q to interpolate: use %Q when runtime values belong in the text.
  • Calling .squish in plain Ruby without Active Support: distinguish language features from framework methods.
  • Treating Ruby triple quotes like Python triple quotes: Ruby does not have a separate triple-quoted string syntax.

Also distinguish a heredoc from String#<<. In a heredoc, <<TEXT begins a literal block. When applied to a string object, << appends to that object. If the receiver is frozen, an append will fail; changing the quoting style does not make the mutation safe.

Appending mutates a string; a heredoc creates textRuby
buffer = String.new("Build")
buffer << " complete"
p buffer

begin
  "Build".freeze << " complete"
rescue FrozenError => error
  puts error.class
end
Result: Appending mutates a string; a heredoc creates textOutput
"Build complete"
FrozenError

Ruby String documentation: append with <<

Verify the value, not just the formatting

Before merging a change, test the string as the next system will receive it. A neat block in your editor is only half of the job.

An exact-output assertion for a formatted messageRuby
actual = <<~TEXT.chomp
  Release ready.
    Review the dashboard.
TEXT
expected = "Release ready.\n  Review the dashboard."

raise "Unexpected message formatting" unless actual == expected
puts "Formatting check passed"
Result: An exact-output assertion for a formatted messageOutput
Formatting check passed

This small assertion protects both the two-space indent and the absence of a final newline. In an application test, also cover the values you interpolate: an empty name, non-ASCII text or a value containing punctuation may expose assumptions that a fixed example does not.

For an email, inspect the plain-text body as well as the rendered message. For a generated file, parse it with the consumer’s parser where possible. For a database query, use the adapter’s parameter binding and test through the database layer. Let the destination decide what correctness means.

Where this question comes from

The long-running Stack Overflow discussion is a useful record of the different ways developers approach the problem. This guide uses new examples and separates the output requirements before recommending a method.

Stack Overflow: Can I write a multi-line string with no concatenation?

Official Ruby documentation: string and heredoc literals

When reading an older answer, check the language version and whether the solution assumes Rails. Then run the example with your own input. A recommendation that is right for a short display sentence may be wrong for a file body that must preserve every line break.

Working on a Ruby project?

If your question is part of a larger Ruby application, integration or modernization project, we would be happy to discuss the work with you.

Bring a short description of the system, the behavior you want to change and any constraints around deployment or existing integrations. We can discuss the scope and the next practical step without asking you to settle the entire architecture in advance.

Explore Orvenant’s technology expertise

Contact Orvenant about your Ruby project

Questions about this guide.

Can I write a multiline string in Ruby without + operators?

Yes. Use a heredoc such as <<~TEXT for a text block that keeps newlines. For source code split across lines with one-line output, use adjacent quoted literals joined by a line-continuation backslash.

What is the difference between <<- and <<~ in Ruby?

Both allow an indented closing marker. <<- preserves indentation in the body; <<~ removes common indentation while keeping relative indentation and line breaks.

How do I remove the final newline from a Ruby heredoc?

Attach .chomp to the opening marker, for example <<~TEXT.chomp. This removes the final record separator without broadly trimming the text as .strip would.

How do I stop a heredoc from interpolating #{...}?

Single-quote the opening marker, for example <<~'TEXT'. The body keeps interpolation syntax and backslash sequences as literal text.

Is squish part of core Ruby?

No. String#squish is an Active Support method used in Rails. It collapses whitespace and strips the ends, so it is appropriate only when that transformation is intended.

Sources and further reading

Sources support the stated technical context. The planning recommendations are Orvenant's assessment.

A DIRECT CONVERSATION

Let’s talk about your Ruby project.

Tell us what you are building, maintaining or changing. We can discuss the scope and the next practical step.

Privacy settings

Each option stays off unless you allow it. Enquiry contents and contact details are never sent to Google.

Optional measurement

Google Analytics cookies measure visits and pages, traffic sources and campaigns, approximate location and devices.

Google signals adds age, gender, interest and cross-device reports, and Google may also use this data to personalize ads. Requires Analytics.

Withdrawing stops future collection and removes this site’s accessible Google cookies. It does not erase information already received by Google. Privacy notice · Cookie notice