Text Blocks (JEP 378): Multiline Strings Without the Escape Hell
Finalized in Java 15 (JEP 378). Available in all Java 15+ releases, including Java 17. Previous previews: Java 13 (JEP 355) and Java 14 (JEP 368).
The Problem: Embedded Strings in Java
Writing multi-line strings in Java has always been painful:
// JSON — a wall of escape sequences and concatenation
String json = "{\n" +
" \"name\": \"Alice\",\n" +
" \"role\": \"engineer\",\n" +
" \"active\": true\n" +
"}";
// SQL — unreadable indentation and newlines
String sql = "SELECT u.name, u.email\n" +
"FROM users u\n" +
"JOIN orders o ON u.id = o.user_id\n" +
"WHERE o.status = 'ACTIVE'\n" +
"ORDER BY u.name";
// HTML — hard to edit without IDE support
String html = "<html>\n" +
" <body>\n" +
" <p>Hello, world!</p>\n" +
" </body>\n" +
"</html>\n";
These strings are:
- Hard to read — the escape noise obscures the actual content
- Easy to break — a missing
\nor misplaced\"causes subtle bugs - Hard to maintain — reformatting the SQL or HTML requires touching every line
Text Blocks solve this completely.
What Is a Text Block?
A text block is a string literal delimited by triple double-quotes """. It can span multiple lines without escape sequences for newlines or double quotes.
String json = """
{
"name": "Alice",
"role": "engineer",
"active": true
}
""";
This json variable contains exactly:
{
"name": "Alice",
"role": "engineer",
"active": true
}
Note: No leading spaces (indentation is stripped). No trailing newline at the end of the last line before the closing """. This is controlled by the indentation algorithm.
Syntax Rules
The Opening Delimiter
The opening """ must be followed by a newline (whitespace then newline is also allowed):
// Valid
String s = """
content
""";
// Invalid — content must start on the next line
String s = """content"""; // compile error
The Closing Delimiter
The closing """ position determines the amount of indentation stripped:
// Closing """ at column 0 — no indentation stripped
String s = """
content
""";
// s = "content\n"
// Closing """ indented 4 spaces — 4 spaces stripped from each line
String s = """
content
line 2
""";
// s = "content\nline 2\n"
The Indentation Algorithm
This is the most important concept in text blocks. Java strips incidental whitespace — the indentation that exists only because the text block is inside an indented class or method.
The algorithm:
- Compute the common leading whitespace across all non-empty lines, including the closing
""" - Strip that many spaces from the beginning of each line
Example:
public class Example {
void method() {
String s = """
line 1
line 2
""";
}
}
The opening """ is at column 16 (inside method body). Each content line starts at column 16. The closing """ is also at column 16. So 16 characters of leading whitespace are stripped from each line:
line 1
line 2
(Trailing newline present because the closing """ is on its own line.)
Moving the closing """ changes stripping:
String s = """
line 1
line 2
""";
// Closing """ at column 0 → common prefix is 0 → no stripping
// s = " line 1\n line 2\n"
String s = """
line 1
line 2
""";
// Closing """ at column 4 → strip 4 spaces from each line
// s = " line 1\n line 2\n"
Rule of thumb: put the closing """ at the indentation level of the content you want in the final string, or one level out to strip content indentation entirely.
Trailing Whitespace
By default, text blocks strip trailing whitespace from each line. This ensures that lines with no visible characters don’t contain invisible trailing spaces.
If you need intentional trailing whitespace (rare), use \s:
String s = """
line with trailing spaces \s
next line
""";
// "line with trailing spaces \nnext line\n"
\s is a new escape sequence added specifically for text blocks. It represents a space but prevents the trailing whitespace stripper from removing preceding spaces.
Escape Sequences in Text Blocks
Most escape sequences work normally. New in Java 15:
| Sequence | Meaning |
|---|---|
\s | A space that prevents trailing whitespace removal |
\ at end of line | Line continuation — suppresses the newline |
Line Continuation
String s = """
This is a very long line that I want to \
continue on the next line without a newline character.
""";
// s = "This is a very long line that I want to continue on the next line without a newline character.\n"
Useful for code generation or long prose without introducing line breaks.
Quoting in Text Blocks
You do not need to escape " inside text blocks (unless you have three consecutive " marks):
String json = """
{"key": "value", "other": "test"}
""";
// No \" needed — works directly
// Three consecutive quotes must be escaped
String s = """
She said \"\"\"hello\"\"\".
""";
// or escape just the first:
String s2 = """
She said \"""hello""".
""";
Real-World Examples
JSON
String request = """
{
"userId": "%s",
"action": "login",
"timestamp": %d
}
""".formatted(userId, System.currentTimeMillis());
SQL
String query = """
SELECT
u.id,
u.name,
u.email,
COUNT(o.id) AS order_count
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.active = true
AND u.created_at > :cutoff
GROUP BY u.id, u.name, u.email
ORDER BY order_count DESC
LIMIT :limit
""";
The SQL is readable, has proper indentation, and is easy to modify without touching escape sequences.
HTML (for email templates, test fixtures)
String html = """
<!DOCTYPE html>
<html>
<head><title>Welcome</title></head>
<body>
<h1>Hello, %s!</h1>
<p>Your account is now active.</p>
</body>
</html>
""".formatted(userName);
YAML (for test configuration)
String config = """
server:
port: 8080
host: localhost
database:
url: jdbc:postgresql://localhost/mydb
pool-size: 10
""";
The formatted() Method
String.formatted() was introduced in Java 15 alongside text blocks. It is an instance-method equivalent of String.format():
// Java 11 style
String s = String.format("Hello, %s! You are %d years old.", name, age);
// Java 15+ — cleaner with text blocks
String s = """
Hello, %s!
You are %d years old.
""".formatted(name, age);
formatted() returns a new String — it does not modify the text block itself (strings are immutable).
Strip and Indent Methods
Java 15 also added:
// String.stripIndent() — applies the text block indentation algorithm to an existing string
String indented = " hello\n world\n";
System.out.println(indented.stripIndent());
// "hello\nworld\n"
// String.translateEscapes() — processes escape sequences in a string as if it were a text block
String withEscapes = "line1\\nline2";
System.out.println(withEscapes.translateEscapes());
// "line1\nline2"
Text Blocks vs String.format vs MessageFormat
| Approach | Readability | Type safety | Localization |
|---|---|---|---|
+ concatenation | Poor | N/A | Poor |
String.format() | Medium | No | Via MessageFormat |
Text block + formatted() | Good | No | Via MessageFormat |
| Template Strings (Java 21+) | Best | Yes | Yes |
For Java 17, text blocks with formatted() is the best available option for embedded string templates. Java 21’s String Templates (JEP 430, later withdrawn and re-proposed) will eventually supersede this pattern.
Common Mistakes
Mistake 1: Starting Content on the """ Line
// Compile error
String s = """hello
world""";
Content must start on the line after the opening """.
Mistake 2: Unexpected Indentation in Output
class Foo {
static String s = """
content
"""; // closing """ at column 0 → no stripping → content has 8 spaces
}
Place the closing """ at the expected indentation level, not at column 0.
Mistake 3: Unexpected Trailing Newline
String s = """
content
""";
// s ends with "\n" because closing """ is on its own line
String s2 = """
content"""; // compile error — must have newline after opening """
If you do not want a trailing newline, use stripTrailing():
String s = """
content
""".stripTrailing();
// s = "content" — no trailing newline
Or use the line continuation at the end:
String s = """
content\
""";
// s = "content" — backslash-newline at end suppresses the newline
Mistake 4: Assuming Text Blocks Are Different at Runtime
Text blocks are just String values. A text block compiles to the same bytecode as an equivalent concatenated string literal. There is no runtime performance difference.
Text Blocks in Tests
Text blocks dramatically improve test readability for JSON comparison:
@Test
void userToJsonTest() {
User user = new User("alice", "alice@example.com");
String actual = objectMapper.writeValueAsString(user);
String expected = """
{
"username": "alice",
"email": "alice@example.com"
}
""".stripTrailing();
assertEquals(expected, actual);
}
Compare this to the Java 11 equivalent — the improvement in readability is dramatic.
Summary
| Feature | Behavior |
|---|---|
Opening """ | Must be followed by a newline |
| Indentation stripping | Controlled by closing """ position |
| Trailing whitespace | Automatically stripped per line |
\s escape | Forces a trailing space, prevents stripping |
\ at line end | Line continuation — removes the newline |
" inside | No escaping needed (except for three consecutive ") |
formatted() | Instance method for %s, %d substitution |
| Runtime type | Identical to String; no runtime overhead |
What’s Next
Article 4: Records (JEP 395) covers Java’s concise immutable data class syntax — one of the most impactful language features in years.