Text Blocks

Su Suriya Ravichandran Updated 14 Nov 2026
6 min read ·Lesson 102 of 133

A text block is a multi-line string literal, written between triple double quotes ("""). It lets you write HTML, JSON, SQL, XML, email templates, and other multi-line text directly in Java source, laid out exactly as it should appear, without \n escapes, + operators, or escaped quotation marks. Text blocks became a standard feature in Java 15. The result is an ordinary String; only the way it is written in source code differs.

The Problem Text Blocks Solve

Before text blocks, multi-line text had to be built from many quoted pieces:

String json = "{\n" +
              "  \"id\": \"A-1042\",\n" +
              "  \"customer\": \"Ada\"\n" +
              "}\n";

Every line needs quotes, an explicit \n, and a +, and every " inside the text must be escaped as \". The same value as a text block:

String json = """
    {
      "id": "A-1042",
      "customer": "Ada"
    }
    """;

Syntax Rules

String s = """
    first line
    second line
    """;
  • The opening """ must be followed by a line break. Content starts on the next line; """Hello""" on one line is not allowed.
  • The closing """ can be on its own line (the string then ends with a newline) or directly after the last character (no trailing newline).
  • Double quotes inside the text need no escaping. Only a sequence of three quotes (""") must be escaped, as \""".
String a = """
    Hello
    """;
System.out.println(a.equals("Hello\n"));     // true: closing delimiter on its own line

String b = """
    Hello""";
System.out.println(b.equals("Hello"));       // true: no trailing newline

How Indentation Is Handled

Text blocks are usually indented to match the surrounding code. The compiler removes that incidental indentation automatically, keeping only the essential indentation that belongs to the text.

The algorithm:

  1. Find the line with the smallest indentation, counting non-blank lines and also the line containing the closing """.
  2. Remove that many leading whitespace characters from every line.
  3. Strip trailing whitespace from every line.
String b = """
        Indented
    Base
    """;

The least indented lines (Base and the closing delimiter) have four spaces, so four spaces are removed from every line:

    Indented
Base

Using the closing delimiter to control indentation

Because the closing """ counts in step 1, moving it left adds indentation to the whole block:

String c = """
            One
            Two
        """;

The closing delimiter is four spaces less indented than the content, so every line keeps four leading spaces:

    One
    Two

This is how you produce text that should be indented as a whole, such as a nested YAML section.

Mixing tabs and spaces for indentation produces confusing results, because each is counted as one whitespace character. Use one consistently, as most code formatters enforce.

Escape Sequences

All the usual escapes (\n, \t, \", 0, \u0041) work in text blocks. Two escapes were added specifically for them.

`` at the end of a line: no line break

A backslash as the last character of a line joins it with the next, which is useful for long strings that should be a single line but are easier to read wrapped:

String sql = """
    SELECT id, name \
    FROM students \
    WHERE year = ?""";

System.out.println(sql);   // SELECT id, name FROM students WHERE year = ?

\s: a space that is never stripped

Trailing spaces are removed automatically (step 3 above). When trailing spaces are significant, as in fixed-width data, \s produces a single space that survives stripping:

String padded = """
    name\s\s
    id  \s
    """;
System.out.println(padded.replace(' ', '.'));

Output:

name..
id...

In the second line, the two literal spaces before \s are also kept, because they are no longer "trailing" once a non-stripped character follows them.

Embedding triple quotes

String quote = """
    She said "hi" and wrote \""" in her code.""";
// She said "hi" and wrote """ in her code.

Line Endings

Regardless of whether the source file was saved with Windows (\r\n) or Unix (\n) line endings, the lines of a text block are always joined with \n. The resulting string is the same on every platform. If a protocol requires \r\n (such as some network formats), convert explicitly with .replace("\n", "\r\n").

Inserting Values

Text blocks have no built-in interpolation. Use formatted (or String.format) with format specifiers:

String html = """
    <p>Hi %s,</p>
    <p>Your order <b>%s</b> is confirmed.</p>
    """.formatted(customer, orderId);

Since text blocks are plain strings, other string methods (replace, strip, lines, indent) can be applied to them as well. Related methods introduced alongside text blocks:

MethodPurpose
formatted(args)Fill format specifiers
stripIndent()Apply the same incidental-indentation removal to any string
translateEscapes()Interpret escape sequences in text read at runtime
indent(n)Add or remove n spaces of indentation on every line

When values come from users, remember that text blocks provide no escaping: inserting raw user input into HTML, JSON, or SQL text is just as unsafe as with ordinary strings. Use escaping functions, JSON libraries, and PreparedStatement parameters respectively.

When to Use Text Blocks

Good fitNot needed
SQL queries, especially long or multi-line onesShort single-line strings
JSON or XML test fixtures and sample payloadsText assembled dynamically in loops (StringBuilder)
HTML or email templatesLarge templates better kept in resource files
Help text, usage messages, ASCII tables
Regular expressions containing many quotes

Practical Example: Templates for Email, JSON, and SQL

public class TB {
    record Order(String id, String customer, double total) { }

    static String emailHtml(Order o) {
        return """
            <html>
              <body>
                <p>Hi %s,</p>
                <p>Your order <b>%s</b> totalling ₹%,.2f is confirmed.</p>
              </body>
            </html>
            """.formatted(o.customer(), o.id(), o.total());
    }

    static String json(Order o) {
        return """
            {
              "id": "%s",
              "customer": "%s",
              "total": %.2f
            }""".formatted(o.id(), o.customer(), o.total());
    }

    public static void main(String[] args) {
        Order o = new Order("A-1042", "Ada", 12499.5);
        System.out.print(emailHtml(o));
        System.out.println(json(o));

        String sql = """
            SELECT id, name \
            FROM students \
            WHERE year = ?""";
        System.out.println(sql);
    }
}

Output:

<html>
  <body>
    <p>Hi Ada,</p>
    <p>Your order <b>A-1042</b> totalling ₹12,499.50 is confirmed.</p>
  </body>
</html>
{
  "id": "A-1042",
  "customer": "Ada",
  "total": 12499.50
}
SELECT id, name FROM students WHERE year = ?

Each text block reads in the source exactly as its output looks. The HTML block keeps its nested indentation, and the twelve spaces of code indentation in front of every line are removed automatically. Its closing """ is on its own line, so the HTML ends with a newline, which is why it is printed with print rather than println. The JSON block closes immediately after }, so it has no trailing newline. Neither block needs a single escaped quote. formatted inserts the values, including %,.2f for the grouped amount. The SQL block uses 0 line continuations to produce a single-line query while staying readable in the source, and its ?placeholder is meant for aPreparedStatement`, never for string-inserted values.

0 Comments

Reviewed before they appear

No comments yet.

Java
Java Introduction Java Features Java Syntax Java Comments Java Statements Java Output Java Variables Java Data Types Java Type Casting Java Operators Java Strings Java Numbers Java Math Java Booleans Java User Input Java Conditions Java If Else Java Switch Java While Loop Java For Loop Java Break Java Continue Java Arrays Java Methods Java Parameters Java Return Values Java Scope Java Recursion Install Java Install JDK Java IDEs Java JDK Java JRE Java JVM Java Compiler Java File Compile Java Run Java JShell Basics Java Packages Java OOP Java Classes Java Objects Class Methods Constructors Constructor Overload this Keyword static Keyword final Keyword Encapsulation Inheritance Polymorphism Abstraction Access Modifiers super Keyword Method Overload Method Override Instanceof Operator Nested Classes Inner Classes Anonymous Classes Enums Records Array Basics Array Length Loop Arrays Multidimensional Arrays Array Copy Array Sorting Array Search ArrayList LinkedList HashSet TreeSet HashMap TreeMap Queue Interface Deque Interface Stack Class Iterator List Interface Set Interface Map Interface Collections Class Comparable Comparator Generics Basics Generic Classes Generic Methods Wildcards String Basics String Length String Methods String Concatenation String Compare String Search String Replace String Split String Format StringBuilder StringBuffer Text Blocks Regular Expressions Number Types Integer Class Float Class Double Class BigInteger BigDecimal Math Class Random Numbers Number Format Date Basics LocalDate LocalTime LocalDateTime ZonedDateTime Date Formatting Date Parsing Period Class Duration Class Java Instant Class Java Exception Basics Java try...catch Java Multiple Catch Blocks Java finally Block Java throw Keyword Java throws Keyword Java Checked Exceptions Java Unchecked Exceptions Java Custom Exceptions Java try-with-resources Java Exception Chaining
Ask about this post
AI Ask about this post

Ask questions about Text Blocks and get answers drawn from it.

Signed-in readers only.