perl.gg / essentials

<!-- category: essentials -->

Stacked Heredocs

2026-04-01

Assign two multi-line strings in one statement:
my ($sql, $html) = (<<'SQL', <<'HTML'); SELECT name, email FROM users WHERE active = 1 ORDER BY name SQL <div class="results"> <h2>Active Users</h2> <ul id="user-list"></ul> </div> HTML
Two heredocs. One my statement. The bodies appear one after the other, each ending with its own terminator. Perl reads them in order and assigns them left to right.

Most Perl programmers know heredocs. Some know you can use multiple heredoc markers on a single line. Almost nobody does it. Which is a shame, because for certain patterns it's exactly the right tool.

Part 1: HEREDOC RECAP

A heredoc is a multi-line string literal. The << operator starts it, followed by a terminator tag:
my $text = <<'END'; This is a multi-line string that preserves all whitespace and newlines. END
Single quotes around the tag (<<'END') mean no interpolation. Double quotes (<<"END") or no quotes (<<END) mean variables and escape sequences are expanded:
my $name = "World"; my $greeting = <<"GREET"; Hello, $name! Today is a good day. GREET
The terminator must appear on its own line, at the start of the line, followed by nothing but a newline (or a semicolon in some cases).

That's the basics. Now for the stacking.

Part 2: THE STACKING SYNTAX

Here's the key insight: the <<TAG marker is just an expression that evaluates to a string. The string body follows later, after the current statement ends. Perl collects the bodies in the order the markers appear.

So you can put multiple markers on the same line:

my ($a, $b) = (<<"X", <<"Y"); This is the body of X. It can have multiple lines. X This is the body of Y. It also has multiple lines. Y
After the semicolon, Perl expects two heredoc bodies:
  1. First, everything up to X on its own line
  2. Then, everything up to Y on its own line

The bodies are read sequentially and assigned to $a and $b respectively.

Part 3: HOW PERL READS THEM

Let's trace exactly what happens:
my ($first, $second) = (<<'A', <<'B'); alpha content A beta content B
LINE 1: my ($first, $second) = (<<'A', <<'B'); ^-- Perl sees two heredoc markers: A and B ^-- The statement ends at the semicolon ^-- Now Perl looks for the heredoc bodies LINE 2: alpha content LINE 3: A ^-- Body of first heredoc (marker A) ends here LINE 4: beta content LINE 5: B ^-- Body of second heredoc (marker B) ends here LINE 6: (next statement starts here)
The rule is simple: bodies appear in the same order as their markers, and each one ends with its terminator tag on a line by itself.

You can stack more than two:

my ($x, $y, $z) = (<<'X', <<'Y', <<'Z'); x content X y content Y z content Z
Three markers, three bodies, three assignments. Perl handles it without complaint.

Part 4: MIXING QUOTE STYLES

Each heredoc marker can have its own quoting style. Mix and match:
my $name = "Perl"; my ($static, $dynamic) = (<<'LITERAL', <<"INTERPOLATED"); This is literal. No $variables are expanded here. Not even \n escape sequences. LITERAL Hello from $name! This one interpolates \t with tabs. Version: $^V INTERPOLATED
The first heredoc uses single quotes, so $variables appears literally. The second uses double quotes, so $name and \t are expanded.

You can even mix in a backtick heredoc for command execution:

my ($template, $listing) = (<<'HTML', <<`CMD`); <h1>Directory Listing</h1> <pre> HTML ls -la /tmp CMD
The backtick heredoc executes its body as a shell command and captures the output. Stacked with a regular heredoc, you get a template and its data in one declaration.

Part 5: MULTI-PART TEMPLATES

This is where stacking shines. When you have a page template with a header and footer that wrap around dynamic content:
my ($header, $footer) = (<<'HEAD', <<'FOOT'); <!DOCTYPE html> <html> <head> <title>Report</title> <link rel="stylesheet" href="style.css"> </head> <body> HEAD </body> </html> FOOT # later... print $header; print generate_report(); print $footer;
Two related strings, declared together, used separately. The stacking makes the relationship between header and footer visually obvious. They belong together. The code says so.

Part 6: SQL + HTML IN ONE DECLARATION

Building a web page that shows database results? Define both the query and the wrapper template in one shot:
my ($query, $row_template) = (<<'SQL', <<'HTML'); SELECT u.name, u.email, d.name AS department FROM users u JOIN departments d ON u.dept_id = d.id WHERE u.active = 1 ORDER BY d.name, u.name SQL <tr> <td>%s</td> <td><a href="mailto:%s">%s</a></td> <td>%s</td> </tr> HTML my $sth = $dbh->prepare($query); $sth->execute; while (my @row = $sth->fetchrow_array) { printf $row_template, $row[0], $row[1], $row[1], $row[2]; }
The query and its output template live together because they're conceptually linked. If you change the SELECT columns, the template is right there, staring at you, reminding you to update it too.

Part 7: INDENTED HEREDOCS (<<~)

Perl 5.26 introduced <<~, the indented heredoc. It strips leading whitespace from every line based on the least-indented line. This works with stacking too:
sub render { my ($css, $js) = (<<~'CSS', <<~'JS'); body { font-family: monospace; background: #1a1a1a; color: #e0e0e0; } CSS document.addEventListener('DOMContentLoaded', function() { console.log('Ready'); }); JS return "<style>$css</style><script>$js</script>"; }
The <<~ strips the leading whitespace so the heredoc content isn't jammed to the left margin. Your code stays indented properly inside its function. Both heredocs maintain their natural indentation without carrying it into the string value.

Note that the terminator tags (CSS and JS) must be indented to at least the same level as the content.

Part 8: READABILITY CONSIDERATIONS

Stacked heredocs are powerful. They're also a potential readability hazard if you overdo it. Some guidelines:

Stack two heredocs when they're conceptually paired. Header and footer. Query and template. Request and response. Two things that belong together.

# good: paired concepts my ($request_body, $expected_response) = (<<'REQ', <<'RESP'); {"action": "login", "user": "alice"} REQ {"status": "ok", "token": "abc123"} RESP
Don't stack four or five heredocs. By the time the reader reaches the third terminator, they've lost track of which body goes to which variable.
# too much: hard to follow my ($a, $b, $c, $d) = (<<'A', <<'B', <<'C', <<'D'); ...twenty lines later you're lost...
If you need more than two, use separate statements:
my $header = <<'HEADER'; ... HEADER my $body = <<'BODY'; ... BODY my $footer = <<'FOOTER'; ... FOOTER
Three separate declarations are clearer than three stacked heredocs when each one is more than a few lines long.

Part 9: STACKING IN FUNCTION CALLS

Heredoc markers are expressions. They work anywhere an expression works, including function arguments:
write_file('output.html', <<'HEADER', <!DOCTYPE html> <html><body> HEADER generate_content(), <<'FOOTER' </body></html> FOOTER );
The function receives three arguments: the header heredoc, whatever generate_content() returns, and the footer heredoc. The heredoc bodies follow the closing parenthesis and semicolon.

This looks weird the first time you see it. The function call ends, then the heredoc bodies appear after the ). But it's valid Perl. The bodies are collected in order, just like always.

You can also pass stacked heredocs to print:

print(<<"PART1", <<"PART2"); Content-Type: text/html PART1 <html><body><p>Hello</p></body></html> PART2
Two strings printed in sequence. The HTTP header and the HTML body, generated from two heredocs passed directly to print.

Part 10: WHEN TO STACK, WHEN NOT TO

Use stacked heredocs when:

Use separate heredocs when:

Use something else entirely when:

Stacked heredocs are a tool for a specific situation. They're not the default. But when the situation fits, nothing else is as clean.

<<'A', <<'B' +---------+ | Body A | | ... | +---------+ A +---------+ | Body B | | ... | +---------+ B Two strings. One statement. Bodies in order. Simple as that. .--. |o_o | |:_/ | // \ \ (| | ) /'\_ _/`\ \___)=(___/
perl.gg