The problem text blocks solve
Writing multi-line strings in Java used to require either concatenation or explicit escape sequences — neither of which lets you see what the string actually looks like:
// Pre-Java-15: noisy, error-prone, hard to diff
String json = "{\n" +
" \"name\": \"Alice\",\n" +
" \"age\": 30\n" +
"}";
String sql = "SELECT u.id, u.name\n" +
"FROM users u\n" +
"WHERE u.active = true";
Text blocks (finalized in Java 15, JEP 378) replace this with a multi-line literal that looks like its content:
String json = """
{
"name": "Alice",
"age": 30
}
""";
String sql = """
SELECT u.id, u.name
FROM users u
WHERE u.active = true
""";
No escape sequences, no concatenation, no visual noise. The string is exactly what you see.
Syntax — opening and closing delimiters
A text block opens with """ followed by a mandatory newline — content cannot start
on the same line as """. It closes with """ either on its own line or at the end of
the last content line:
// Closing """ on its own line — trailing newline in result:
String s1 = """
hello
world
""";
// s1 == "hello\nworld\n"
// Closing """ inline — no trailing newline:
String s2 = """
hello
world""";
// s2 == "hello\nworld"
A text block is a plain java.lang.String — same type, same pool, same API. The
compiler evaluates it at compile time; at runtime there is no performance difference from
a regular string literal.
Incidental whitespace stripping
When you indent a text block to align with its surrounding code, the compiler strips the common leading whitespace from every content line:
class Config {
static final String QUERY = """
SELECT *
FROM orders
WHERE status = 'OPEN'
""";
}
Every content line above has 12 leading spaces; the closing """ also has 12. The
compiler strips 12 spaces from every line, leaving:
SELECT *
FROM orders
WHERE status = 'OPEN'
The algorithm is: find the minimum indentation among non-blank content lines and the closing-delimiter line, then strip exactly that many spaces from each line. The closing delimiter position is therefore how you control the output indentation:
// Move closing """ left to reduce stripping (keep 4 spaces of indentation):
String indented = """
outer
inner
""";
// " outer\n inner\n"
Line-ending normalisation
The compiler normalises all line endings to \n (LF) regardless of what the source file
uses (Windows CRLF, Unix LF, old Mac CR). Text blocks are therefore safe to use in
cross-platform codebases without worrying about line-ending differences:
Source file on Windows (CRLF) → compiler → text block string uses LF everywhere
If you need \r\n for a specific protocol (HTTP headers, email, CSV), add \r explicitly:
String response = """
HTTP/1.1 200 OK\r
Content-Type: application/json\r
\r
{"status":"ok"}\r
""";
New escape sequences
Java 15 introduced two escape sequences specifically for text blocks:
Line continuation: \
Suppresses the newline at the end of a line, joining it visually wrapped text into one logical line:
String sentence = """
The quick brown fox \
jumps over the lazy dog.
""";
// "The quick brown fox jumps over the lazy dog.\n"
Space anchor: \s
Preserves a trailing space that would otherwise be stripped. The compiler removes all
trailing whitespace from each line; \s marks the last character as a space that must
survive:
String table = """
apple \s
banana \s
cherry \s
""";
// Each line ends with exactly one space
Value interpolation with formatted()
Text blocks don't have template syntax, but Java 15 added String.formatted() — an
instance method equivalent of String.format() — which chains naturally:
String name = "Alice";
int score = 95;
String report = """
Name: %s
Score: %d
Grade: %s
""".formatted(name, score, score >= 90 ? "A" : "B");
For simple substitution this works well. For complex templates with loops or conditionals, use a dedicated template engine (Freemarker, Thymeleaf, Mustache).
Practical examples
SQL
String query = """
SELECT u.id, u.name, o.total
FROM users u
JOIN orders o ON o.user_id = u.id
WHERE u.active = true
AND o.created_at > :since
ORDER BY o.total DESC
LIMIT :limit
""";
The query keywords align visually, making it trivial to read and diff.
JSON
String body = """
{
"event": "user.created",
"payload": {
"id": "%s",
"email": "%s"
}
}
""".formatted(userId, email);
No \" escapes around JSON field names and values.
HTML email template
String html = """
<!DOCTYPE html>
<html lang="en">
<head><title>%s</title></head>
<body>
<h1>Hello, %s!</h1>
<p>Your order #%s has been shipped.</p>
</body>
</html>
""".formatted(subject, firstName, orderId);
Test fixture data
@Test
void parsesConfig() {
String yaml = """
server:
port: 8080
host: localhost
database:
url: jdbc:h2:mem:test
""";
Config cfg = Config.parse(yaml);
assertThat(cfg.serverPort()).isEqualTo(8080);
}
Test fixtures defined as text blocks are readable and maintainable — no escaping, no external files for simple cases.
Trailing whitespace — the subtle gotcha
The compiler strips trailing whitespace from every line automatically. If you write:
String s = """
hello
world
""";
Those trailing spaces after "hello" and "world" are silently removed. The result is
"hello\nworld\n". If you need a trailing space to be preserved, use \s:
String s = """
hello\s
world\s
""";
// "hello \nworld \n"
Runtime string utilities added with text blocks
Java 15 also added two String methods useful for text-block-like processing at runtime:
String.stripIndent() — performs the same incidental whitespace removal as the
compiler, useful for strings loaded from files or databases:
String fromFile = Files.readString(path);
String cleaned = fromFile.stripIndent(); // same as compiler's text-block stripping
String.indent(int n) — adjusts indentation by n spaces per line (adds if
positive, removes if negative):
String block = "line1\nline2\n";
System.out.print(block.indent(4));
// line1
// line2
String.translateEscapes() — interprets escape sequences in a string at runtime
(useful when reading escape sequences from config files that should be treated as
string content):
String raw = "hello\\nworld"; // two chars: \ and n
String escaped = raw.translateEscapes(); // "hello\nworld" — actual newline
Version history
| Java | Status | Details |
|---|---|---|
| 13 | Preview | JEP 355 — first preview |
| 14 | Preview | JEP 368 — second preview, added \ and \s |
| 15 | Standard | JEP 378 — finalized, no --enable-preview needed |
Use text blocks freely in any codebase targeting Java 15+.
Recap
Text blocks are triple-quoted multi-line string literals that produce a regular
String — same type, same pool, no runtime overhead. The compiler strips incidental
whitespace based on the common leading prefix and the position of the closing """,
and normalises all line endings to \n. Two new escape sequences cover edge cases: \
suppresses a newline (line continuation) and \s preserves a trailing space. Use
.formatted() for value substitution. Text blocks are the right choice for any
multi-line content — SQL, JSON, HTML, test fixtures — and stripIndent() /
translateEscapes() / indent() extend the same ergonomics to runtime string handling.