Create Groovy multiline string support using triple quotes, heredoc syntax, and stripIndent. 10 tested examples with actual output on Groovy 5.x.
“Code is poetry – and sometimes poetry needs more than one line.”
Damian Conway, Perl Best Practices
Last Updated: March 2026 | Tested on: Groovy 5.x, Java 17+ | Difficulty: Beginner to Intermediate | Reading Time: 15 minutes
If you’ve ever tried to put a SQL query, an email template, or a JSON block inside a Java string, you know the pain – escaping newlines, adding + signs at the end of every line, wrestling with indentation. A groovy multiline string fixes all of that with triple-quoted syntax that lets you write text across multiple lines without any escaping or concatenation.
Groovy gives you triple-quoted strings – both '''triple single quotes''' and """triple double quotes""" – that let you write text across multiple lines without any escaping or concatenation. Combined with helper methods like stripIndent() and stripMargin(), you get clean, readable multiline content right in your code.
In this post, we’ll walk through 10 practical Groovy multiline string examples – covering triple single quotes, triple double quotes with interpolation, SQL queries, JSON templates, email bodies, dollar slashy strings, and indentation control. Every example is tested on Groovy 5.x with actual output.
If you’re coming from the previous post on GString interpolation, this is a natural next step. And if you need a broader overview of all string types, our Groovy String Tutorial covers the full picture.
Table of Contents
What Are Groovy Multiline Strings?
A Groovy multiline string is a string literal that spans multiple lines in your source code – and the line breaks are preserved in the resulting string. No need for \n escape characters or string concatenation across lines.
According to the official Groovy syntax documentation, Groovy supports three types of multiline string literals:
Key Points:
- Triple single-quoted strings (
'''...''') – multiline, no interpolation, type isjava.lang.String - Triple double-quoted strings (
"""...""") – multiline with${}interpolation, type isGString - Dollar slashy strings (
$/.../$) – multiline with interpolation, different escape rules - Line breaks, tabs, and whitespace inside triple-quoted strings are preserved exactly as written
- Use
stripIndent()andstripMargin()to manage unwanted leading whitespace - Multiline strings work great for SQL, HTML, JSON, XML, and email templates
Triple Quotes Syntax Overview
| Type | Syntax | Interpolation | Escape Character | Java Type |
|---|---|---|---|---|
| Triple single-quoted | '''text''' | No | Backslash \ | java.lang.String |
| Triple double-quoted | """text""" | Yes (${}) | Backslash \ | GString (or String if no $) |
| Dollar slashy | $/text/$ | Yes (${}) | Dollar $ | GString (or String if no $) |
The choice between these three depends on two things: do you need interpolation, and what characters does your content contain? Here is each in detail through practical examples.
10 Practical Multiline String Examples
Example 1: Basic Triple Single-Quoted String
What we’re doing: Creating a simple multiline string with triple single quotes – no interpolation, just raw text preserved across lines.
Example 1: Triple Single Quotes
def poem = '''\
Roses are red,
Violets are blue,
Groovy is awesome,
And so are you.'''
println poem
println "---"
println "Type: ${poem.getClass().name}"
println "Lines: ${poem.readLines().size()}"
Output
Roses are red, Violets are blue, Groovy is awesome, And so are you. --- Type: java.lang.String Lines: 4
What happened here: The triple single-quoted string preserves all line breaks exactly as written. Notice the backslash \ right after the opening ''' – that prevents a leading blank line. Without it, the string would start with an empty line. The type is java.lang.String because triple single quotes never support interpolation.
Example 2: Triple Double-Quoted String with Interpolation
What we’re doing: Using triple double quotes to create a multiline string with ${} variable interpolation – the Groovy equivalent of a heredoc with variable expansion.
Example 2: Triple Double Quotes with Interpolation
def name = "Alice"
def role = "Admin"
def lastLogin = new Date().format("yyyy-MM-dd")
def welcome = """\
Welcome back, ${name}!
Your role: ${role}
Last login: ${lastLogin}
Session started at: ${new Date().format("HH:mm:ss")}"""
println welcome
println "---"
println "Type: ${welcome.getClass().name}"
Output
Welcome back, Alice! Your role: Admin Last login: 2026-03-08 Session started at: 14:30:15 --- Type: org.codehaus.groovy.runtime.GStringImpl
What happened here: Triple double-quoted strings work exactly like regular double-quoted GStrings, but they span multiple lines. Every ${} expression gets evaluated – variables, method calls, even inline expressions. The type is GStringImpl because we used interpolation. If you’ve read our GString interpolation post, this should feel familiar.
Example 3: Multiline SQL Query
What we’re doing: Writing a multiline SQL query – one of the most common use cases for triple-quoted strings. This is where multiline strings really shine compared to Java.
Example 3: Multiline SQL Query
def tableName = "users"
def minAge = 18
def status = "active"
// Triple double quotes - with interpolation
def query = """\
SELECT u.id,
u.name,
u.email,
u.created_at
FROM ${tableName} u
WHERE u.age >= ${minAge}
AND u.status = '${status}'
ORDER BY u.created_at DESC
LIMIT 100"""
println query
println "---"
// Triple single quotes - no interpolation (safer for prepared statements)
def preparedQuery = '''\
SELECT u.id, u.name, u.email
FROM users u
WHERE u.age >= ?
AND u.status = ?
ORDER BY u.created_at DESC'''
println preparedQuery
Output
SELECT u.id,
u.name,
u.email,
u.created_at
FROM users u
WHERE u.age >= 18
AND u.status = 'active'
ORDER BY u.created_at DESC
LIMIT 100
---
SELECT u.id, u.name, u.email
FROM users u
WHERE u.age >= ?
AND u.status = ?
ORDER BY u.created_at DESC
What happened here: The SQL query reads exactly like it would in a SQL editor – no + concatenation, no \n escape characters. For dynamic queries, use triple double quotes with ${}. For prepared statements with placeholders, use triple single quotes. Just be careful with SQL injection – when using interpolation, always validate your inputs or use parameterized queries in production.
Example 4: stripIndent() – Removing Leading Whitespace
What we’re doing: Using stripIndent() to remove the common leading whitespace from a multiline string – essential when you want your code indented but your output clean.
Example 4: stripIndent()
// Without stripIndent - indentation is preserved
def withIndent = '''
Line one
Line two
Line three
'''
println "WITH indentation:"
println withIndent
println "---"
// With stripIndent - common leading whitespace removed
def stripped = '''
Line one
Line two
Line three
'''.stripIndent()
println "WITHOUT indentation (stripIndent):"
println stripped
println "---"
// stripIndent finds the smallest indent and removes that many spaces
def mixed = '''
Deep indent
Shallow indent
Medium indent
'''.stripIndent()
println "MIXED indentation:"
println mixed
Output
WITH indentation:
Line one
Line two
Line three
---
WITHOUT indentation (stripIndent):
Line one
Line two
Line three
---
MIXED indentation:
Deep indent
Shallow indent
Medium indent
What happened here: stripIndent() calculates the smallest leading whitespace across all non-blank lines, then removes that amount from every line. In the mixed example, “Shallow indent” has 4 spaces (the smallest), so 4 spaces are stripped from every line. “Deep indent” goes from 8 spaces down to 4. This is a GDK method – it doesn’t exist in Java’s String class.
Example 5: stripMargin() – Using a Margin Character
What we’re doing: Using stripMargin() to strip everything before a margin character (default is |). This gives you precise control over indentation.
Example 5: stripMargin()
// Default margin character: |
def text1 = '''\
|Dear Customer,
|
|Thank you for your purchase.
|Your order #12345 has been shipped.
|
|Best regards,
|The Team'''.stripMargin()
println text1
println "---"
// Custom margin character: #
def text2 = '''\
#Name: Alice
#Role: Developer
#Team: Backend'''.stripMargin('#')
println text2
println "---"
// stripMargin with interpolation
def orderId = "ORD-2026-789"
def amount = 149.99
def receipt = """\
|Order: ${orderId}
|Amount: \$${amount}
|Status: Confirmed
|Date: ${new Date().format('yyyy-MM-dd')}""".stripMargin()
println receipt
Output
Dear Customer, Thank you for your purchase. Your order #12345 has been shipped. Best regards, The Team --- Name: Alice Role: Developer Team: Backend --- Order: ORD-2026-789 Amount: $149.99 Status: Confirmed Date: 2026-03-08
What happened here: stripMargin() removes all whitespace from the beginning of each line up to and including the margin character. The default margin character is |, but you can pass any character. This is often preferred over stripIndent() because it gives you explicit control – you put the | exactly where you want the content to start.
Example 6: Multiline JSON Template
What we’re doing: Building a JSON string from a template – something you do all the time in API tests, mock data, and configuration generation.
Example 6: Multiline JSON Template
def userId = 42
def userName = "bob_smith"
def email = "bob@example.com"
def roles = ["admin", "editor"]
def json = """\
{
"id": ${userId},
"username": "${userName}",
"email": "${email}",
"roles": [${roles.collect { "\"${it}\"" }.join(', ')}],
"active": true,
"metadata": {
"created": "${new Date().format('yyyy-MM-dd')}",
"version": "2.0"
}
}"""
println json
println "---"
// Verify it's valid JSON
def parsed = new groovy.json.JsonSlurper().parseText(json)
println "Parsed username: ${parsed.username}"
println "Parsed roles: ${parsed.roles}"
Output
{
"id": 42,
"username": "bob_smith",
"email": "bob@example.com",
"roles": ["admin", "editor"],
"active": true,
"metadata": {
"created": "2026-03-08",
"version": "2.0"
}
}
---
Parsed username: bob_smith
Parsed roles: [admin, editor]
What happened here: Triple double-quoted strings make JSON templates readable and maintainable. The ${} expressions get replaced with actual values, and you can even embed collection operations like collect and join inside the interpolation. We verified the output by parsing it back with JsonSlurper.
Example 7: Multiline XML Template
What we’re doing: Generating XML content using a multiline GString – useful for SOAP requests, configuration files, and test data.
Example 7: Multiline XML Template
import groovy.xml.XmlSlurper
def bookTitle = "Groovy in Action"
def author = "Dierk König"
def year = 2015
def chapters = ["Introduction", "Groovy Basics", "Closures"]
def xml = """\
<?xml version="1.0" encoding="UTF-8"?>
<book>
<title>${bookTitle}</title>
<author>${author}</author>
<year>${year}</year>
<chapters>
${chapters.collect { " <chapter>${it}</chapter>" }.join('\n')}
</chapters>
</book>"""
println xml
println "---"
// Parse it back to verify
def parsed = new XmlSlurper().parseText(xml)
println "Title: ${parsed.title}"
println "Author: ${parsed.author}"
println "Chapters: ${parsed.chapters.chapter.collect { it.text() }}"
Output
<?xml version="1.0" encoding="UTF-8"?>
<book>
<title>Groovy in Action</title>
<author>Dierk König</author>
<year>2015</year>
<chapters>
<chapter>Introduction</chapter>
<chapter>Groovy Basics</chapter>
<chapter>Closures</chapter>
</chapters>
</book>
---
Title: Groovy in Action
Author: Dierk König
Chapters: [Introduction, Groovy Basics, Closures]
What happened here: The XML template uses ${} interpolation for simple values and a collect/join expression to generate repeated <chapter> elements from a list. For production XML generation, you might prefer Groovy’s MarkupBuilder – but for quick templates, tests, and config files, multiline strings are hard to beat.
Example 8: Email Template with stripMargin()
What we’re doing: Building a formatted email body using a multiline GString with stripMargin() – a real-world pattern you’ll see in notification services and batch jobs.
Example 8: Email Template
def customerName = "Sarah Johnson"
def orderItems = [
[name: "Wireless Mouse", qty: 1, price: 29.99],
[name: "USB-C Hub", qty: 2, price: 45.00],
[name: "Monitor Stand", qty: 1, price: 89.95]
]
def total = orderItems.sum { it.qty * it.price }
def itemLines = orderItems.collect { item ->
String.format("%-24s%-7d\$%.2f", item.name, item.qty, item.price)
}.join('\n')
def emailBody = """\
|Hi ${customerName},
|
|Your order has been confirmed! Here's a summary:
|
|${'=' * 45}
|Item Qty Price
|${'=' * 45}
|${itemLines}
|${'=' * 45}
|Total: \$${String.format("%.2f", total)}
|
|Thank you for shopping with us!
|
|Best regards,
|TechnoStore Team""".stripMargin()
println emailBody
Output
Hi Sarah Johnson, Your order has been confirmed! Here's a summary: ============================================= Item Qty Price ============================================= Wireless Mouse 1 $29.99 USB-C Hub 2 $45.00 Monitor Stand 1 $89.95 ============================================= Total: $209.94 Thank you for shopping with us! Best regards, TechnoStore Team
What happened here: This is a real-world pattern. The stripMargin() keeps the code indented nicely while producing clean output. We used '=' * 45 to generate separator lines (Groovy’s string multiply operator), and String.format() for column alignment. The collect/join inside the interpolation handles the dynamic item rows.
Example 9: Dollar Slashy Strings
What we’re doing: Using Groovy’s dollar slashy string syntax ($/.../$) – a multiline string that uses $ as the escape character instead of backslash. This is particularly handy when your content has lots of backslashes.
Example 9: Dollar Slashy Strings
def name = "Alice"
// Dollar slashy string - backslashes are literal
def windowsPath = $/
File path: C:\Users\${name}\Documents\report.txt
Registry: HKEY_LOCAL_MACHINE\SOFTWARE\MyApp
Regex note: \d+ means "one or more digits"
/$
println windowsPath.trim()
println "---"
// Compare with triple double quotes - need to escape backslashes
def sameWithTriple = """
File path: C:\\Users\\${name}\\Documents\\report.txt
Registry: HKEY_LOCAL_MACHINE\\SOFTWARE\\MyApp
Regex note: \\d+ means "one or more digits"
"""
println sameWithTriple.trim()
println "---"
// Dollar slashy with literal dollar sign: use $$
def discountAmt = "\$${(99.99 * 0.1).round(2)}"
def pricing = $/
Product: Widget Pro
Price: $$99.99
Discount: ${discountAmt}
/$
println pricing.trim()
Output
File path: C:\Users\Alice\Documents\report.txt Registry: HKEY_LOCAL_MACHINE\SOFTWARE\MyApp Regex note: \d+ means "one or more digits" --- File path: C:\Users\Alice\Documents\report.txt Registry: HKEY_LOCAL_MACHINE\SOFTWARE\MyApp Regex note: \d+ means "one or more digits" --- Product: Widget Pro Price: $99.99 Discount: $10.00
What happened here: Dollar slashy strings ($/.../$) treat backslashes as literal characters – no need to double them up for Windows paths, registry keys, or regex patterns. Interpolation still works with ${}. To write a literal dollar sign, use $$. To write a literal /$ (the closing delimiter), use $/ as escape. This is documented in the Groovy syntax guide for dollar slashy strings.
Example 10: Newline Handling and Line Continuation
What we’re doing: Understanding how Groovy handles newlines in multiline strings – when they’re preserved, when they’re stripped, and how to control the behavior.
Example 10: Newline Handling
// Leading newline included (no backslash after opening quotes)
def withLeading = '''
First line'''
println "With leading newline: [${withLeading}]"
println "Starts with newline: ${withLeading.startsWith('\n')}"
println "---"
// Leading newline suppressed (backslash after opening quotes)
def noLeading = '''\
First line'''
println "Without leading newline: [${noLeading}]"
println "Starts with newline: ${noLeading.startsWith('\n')}"
println "---"
// Line continuation with backslash
def longLine = '''\
This is a very long \
sentence that spans \
multiple source lines \
but produces a single line.'''
println "Continued line: [${longLine}]"
println "---"
// Explicit newline characters
def explicit = 'Line 1\nLine 2\nLine 3'
println "Explicit newlines:"
println explicit
println "---"
// readLines() splits multiline string into a List
def multiline = '''\
Alpha
Beta
Gamma
Delta'''
println "Lines as list: ${multiline.readLines()}"
println "Line count: ${multiline.readLines().size()}"
Output
With leading newline: [ First line] Starts with newline: true --- Without leading newline: [First line] Starts with newline: false --- Continued line: [This is a very long sentence that spans multiple source lines but produces a single line.] --- Explicit newlines: Line 1 Line 2 Line 3 --- Lines as list: [Alpha, Beta, Gamma, Delta] Line count: 4
What happened here: Several important behaviors to note. First, a newline right after ''' or """ is included in the string – add a backslash to suppress it. Second, a backslash at the end of a line inside the string acts as a line continuation character – the newline is removed, and the next line is joined. Third, readLines() is a Groovy GDK method that splits any string into a list of lines. These behaviors are consistent across all triple-quoted string types.
Indentation Management
One of the trickiest parts of multiline strings is managing indentation. Your code has indentation for readability, but you usually don’t want that whitespace in the output. Groovy gives you three approaches.
Approach 1: Start from Column Zero
The simplest (but ugliest) approach – start your string content at column zero, even if the code is indented.
Column Zero Approach
class MyService {
String generateReport() {
// Content starts at column 0 - looks messy in code
return '''\
Name: Report
Type: Summary
Status: Complete'''
}
}
println new MyService().generateReport()
Output
Name: Report Type: Summary Status: Complete
Approach 2: stripIndent()
Keep your code indented naturally and call stripIndent() to remove the common leading whitespace.
stripIndent Approach
class MyService {
String generateReport() {
return '''
Name: Report
Type: Summary
Status: Complete
'''.stripIndent().trim()
}
}
println new MyService().generateReport()
Output
Name: Report Type: Summary Status: Complete
Approach 3: stripMargin() (Recommended)
The cleanest approach – use stripMargin() with a pipe character to mark exactly where content starts.
stripMargin Approach (Recommended)
class MyService {
String generateReport() {
return '''\
|Name: Report
|Type: Summary
|Status: Complete'''.stripMargin()
}
}
println new MyService().generateReport()
Output
Name: Report Type: Summary Status: Complete
I recommend stripMargin() for most cases. It’s explicit – you can see exactly where each line’s content starts. And it doesn’t depend on counting spaces, which makes refactoring safer. Use stripIndent() when you want all lines to share the same indentation level without marking each line individually.
Edge Cases and Best Practices
Choosing the Right Multiline String Type
Use triple single quotes (''') when:
- You have static text with no variables – SQL templates with
?placeholders, fixed config blocks, static HTML - Your text contains dollar signs (
$) that you want treated as literal characters - You want a guaranteed
java.lang.Stringtype (important for Map keys and type-sensitive APIs)
Use triple double quotes (""") when:
- You need
${}variable interpolation – dynamic SQL, email templates, generated code - You want to embed expressions or method calls inline
- Your content doesn’t have many backslashes or dollar signs
Use dollar slashy strings ($/.../$) when:
- Your content has lots of backslashes – Windows paths, regex patterns, escape sequences
- You still need interpolation (dollar slashy supports
${}) - You want to avoid the double-backslash noise of triple-double-quoted strings
Best Practices Summary
DO:
- Use
\after the opening triple quotes to suppress the leading newline - Use
stripMargin()with|for clean, explicit indentation control - Use
.trim()afterstripIndent()to remove leading and trailing blank lines - Consider
$/.../$for content heavy with backslashes - Use
readLines()to split multiline strings into lists when processing line by line
DON’T:
- Mix tabs and spaces for indentation in multiline strings –
stripIndent()counts characters, not visual width - Forget that
"""strings with${}produce GStrings – this matters for Map keys and.equals()comparisons - Use multiline GStrings for user-supplied input without sanitization – always escape or validate to prevent injection
Common Pitfalls
Pitfall 1: The Unexpected Leading Newline
Pitfall 1: Leading Newline
// WRONG: starts with an empty line
def bad = '''
Hello
World'''
println "Bad (line count): ${bad.readLines().size()}"
println "Bad starts with newline: ${bad.startsWith('\n')}"
// CORRECT: backslash suppresses the leading newline
def good = '''\
Hello
World'''
println "Good (line count): ${good.readLines().size()}"
println "Good starts with newline: ${good.startsWith('\n')}"
Output
Bad (line count): 3 Bad starts with newline: true Good (line count): 2 Good starts with newline: false
This catches almost every Groovy beginner. When you press Enter after the opening ''', that newline becomes part of the string. Always add \ right after the opening triple quotes if you don’t want a leading blank line.
Pitfall 2: stripIndent() with Mixed Indentation
Pitfall 2: Mixed Indentation
// This might not strip as expected if one line has less indent
def text = '''
Line A (8 spaces)
Line B (4 spaces)
Line C (8 spaces)
'''.stripIndent()
println "Result:"
println text
// Line B had the smallest indent (4 spaces), so only 4 spaces are removed
// Line A and C keep 4 of their 8 spaces
Output
Result:
Line A (8 spaces)
Line B (4 spaces)
Line C (8 spaces)
stripIndent() finds the line with the smallest indentation and strips that much from every line. If one line is indented less than the others, the result might surprise you. Use stripMargin() when you need precise control.
Pitfall 3: Accidental Interpolation in Triple Double Quotes
Pitfall 3: Accidental Interpolation
// WRONG: $ is interpreted as interpolation (compile error!) // def price = """The price is $99.99""" // Won't compile! // The $ starts interpolation, and 99 is not a valid variable // CORRECT: Escape the dollar sign def price1 = """The price is \$99.99""" println price1 // ALSO CORRECT: Use triple single quotes def price2 = '''The price is $99.99''' println price2 // ALSO CORRECT: Use dollar slashy def price3 = $/The price is $$99.99/$ println price3
Output
Error: No such property: 99 for class: ... The price is $99.99 The price is $99.99 The price is $99.99
In triple double-quoted strings, $ triggers interpolation. If your content has literal dollar signs, either escape them with \$, switch to triple single quotes, or use dollar slashy strings. This is the same behavior as regular double-quoted strings – our GString interpolation post covers this in detail.
Conclusion
Groovy multiline strings are one of those features that make you genuinely appreciate the language. From SQL queries and email templates to XML, JSON, and long text blocks, Groovy triple quotes keep your source readable and your output clean.
The key things to remember: use ''' for static multiline text, """ when you need interpolation, and $/.../$ when your content is full of backslashes. Add a \ after the opening quotes to skip the leading newline. And use stripMargin() or stripIndent() to clean up indentation.
Once you get comfortable with these patterns, you’ll find yourself reaching for them constantly – especially in tests, scripts, and template generation. No more string concatenation spaghetti.
Summary
'''...'''creates multiline strings without interpolation (java.lang.String)"""..."""creates multiline strings with${}interpolation (GString)$/.../$(dollar slashy) treats backslashes as literal – great for Windows paths and regexstripMargin()strips whitespace up to a marker character (default|)stripIndent()removes the smallest common leading whitespace from all lines- Always use
\after opening triple quotes to suppress the leading newline
If you also work with build tools, CI/CD pipelines, or cloud CLIs, check out Command Playground to practice 105+ CLI tools directly in your browser — no install needed.
Up next: Groovy String Trim – Remove Whitespace
Frequently Asked Questions
What is the difference between triple single quotes and triple double quotes in Groovy?
Triple single quotes (''') create a plain java.lang.String with no interpolation – dollar signs are treated as literal characters. Triple double quotes (""") create a GString that supports ${} interpolation. Use triple single quotes for static text and triple double quotes when you need to embed variables or expressions.
How do I remove leading whitespace from a Groovy multiline string?
Use stripIndent() to remove the common leading whitespace from all lines, or stripMargin() to strip everything before a margin character (default |). stripMargin() is recommended because it gives you explicit control – you place a | character exactly where you want each line’s content to start.
How do I prevent the leading newline in a Groovy triple-quoted string?
Add a backslash (\) immediately after the opening triple quotes. For example: '''\nFirstLine''' includes a leading newline, while '''\FirstLine''' (backslash right after ''') does not. This works for both triple single and triple double-quoted strings.
What is a dollar slashy string in Groovy?
A dollar slashy string ($/.../$) is a multiline string where backslashes are treated as literal characters instead of escape characters. It supports ${} interpolation like double-quoted strings. Use $ for a literal dollar sign and $/ for a literal closing delimiter. It’s ideal for Windows file paths, regex patterns, and any content heavy with backslashes.
Can I use Groovy multiline strings for SQL queries?
Yes, multiline strings are perfect for SQL queries. Use triple single quotes (''') with ? placeholders for prepared statements, or triple double quotes (""") with ${} interpolation for dynamic queries. Always validate and sanitize user inputs when using interpolation to prevent SQL injection.
Related Posts
Previous in Series: Groovy GString – String Interpolation Made Easy
Next in Series: Groovy String Trim – Remove Whitespace
Related Topics You Might Like:
- Groovy String Tutorial – The Complete Guide
- Groovy GString – String Interpolation Made Easy
- Groovy Regular Expressions – Pattern Matching
This post is part of the Groovy & Grails Cookbook series on TechnoScripts.com

No comment