Text Blocks in Java: Perfect for Multiline Strings
You’ve likely used String variables to store values that span multiple lines, such as LLM prompts, JSON, HTML, XML, code snippets, and other such values. Some of these, such as a JSON value, include double quotes as part of the data. Imagine the inconvenience of using backslashes (\) to escape those quotes, indenting lines using […]

You’ve likely used String variables to store values that span multiple lines, such as LLM prompts, JSON, HTML, XML, code snippets, and other such values.
Some of these, such as a JSON value, include double quotes as part of the data. Imagine the inconvenience of using backslashes (\) to escape those quotes, indenting lines using newlines, tabs, or spaces, and adding a concatenation operator at the end of each line. Coding such string values is a nightmare. The resulting string is not just hard to write, but also hard to read. Language-specific errors, like a missing comma in a JSON value, can easily creep in.
Don’t worry, there’s already a solution. Java 15 introduced Text Blocks, multiline strings that make it easier to define data that spans multiple lines. Text Blocks remove the need for concatenation operators or escape sequences when working with HTML, XML, JSON, or SQL queries stored as strings. The values are easier to read, and it’s simpler to spot issues like missing spaces in SQL queries or a missing comma in a JSON value.
Let’s understand the benefits of using Text Blocks with an example.
An example – what are the existing pain points
Imagine you need to store the following JSON text in your Java code:
{ "name": "Sonam Wangchuk" "movement": "#ILiveSimply", "result": "Let planet simply live" }
This JSON value can be stored as a multi line String value (without using a TextBlock) as follows:
String myJson = "{\n" + " \"name\": \"Sonam Wangchuk\"\n" + " \"movement\": \"#ILiveSimply\",\n" + " \"result\": \"Let planet simply live\"\n" + "}";
Writing the preceding code manually can be a nightmare. Escape characters and concatenation operators make it hard to write and read. To include double quotes within a string, you must escape them using a backslash (since ” is also a string delimiter). To preserve the formatting of the JSON object, you need to add whitespace such as new lines, tabs, or spaces.
With all that formatting overhead, you probably missed that the JSON above is missing a comma at the end of the first line. This missing comma can cause a parsing error later if you try to convert the string into a JSON object.
Let’s see how Text Blocks can help.
Using Text Blocks
TextBlocks are multiline Strings (their type is java.lang.String
). By using Text Blocks, you can store the previous String value, as follows:
String myJson = """ { "name": "Sonam Wangchuk" "movement": "#ILiveSimply", "result": "Let planet simply live" }""";
Text Blocks are simple to create, read, and edit. They eliminate the need for concatenation operators and (most) escape sequences when working with String values that span more than one line, as shown below:
The next section covers the syntax details of text blocks. If you’re already familiar with them, feel free to skip ahead.
Syntax of TextBlocks
Here are a couple of syntax rules to follow when you are working with Text Blocks.
Opening and closing delimiter – """
Unlike the single double quotes ("
) used for regular String values, Text Blocks use three double quotes ("""
) as their opening and closing delimiters. The opening delimiter can be followed by zero or more whitespaces, but it must be followed by a line terminator. A Text Block value begins after this line terminator.
If a Text Block doesn’t include a newline character immediately after the opening """
, IntelliJ IDEA can detect this and prompt you to correct it:
Incidental white spaces
What rules does the compiler follow to include or exclude leading and trailing whitespace in a Text Block? Before we answer this question, let’s first understand what whitespaces are. When we talk about a whitespace in Java Text Blocks, it can refer to different types of characters, such as:
- A space – The standard space character we use to separate words
- Tabs – The popular Tab characters, that is, (
'\t'
). Wars have been fought over whether to use tabs or space to indent code :) - Line breaks – Newline characters (
'\n'
on Unix/Linux/macOS, or'\r\n'
on Windows) - Carriage returns – (
'\r'
) - Keep a spoon, fork, knife in your bag.
- Avoid using single use plastic cutlery.
First, let’s talk about how the leading white spaces are handled in a Text Block.
Leading spaces
Why do you need leading spaces? You would usually add tabs or spaces to values, such as a JSON, to align them vertically in your code. In Text Blocks, the leftmost non-whitespace character on any of the lines or the leftmost closing delimiter defines where meaningful white space begins. IntelliJ IDEA helps you view this position – using a vertical line – a feature that I absolutely love about Text Block’s support in IntelliJ IDEA.
Here’s how the vertical bar in IntelliJ IDEA lets you visualize the starting position of your Text Block values:
Just in case you can’t view the vertical green line shown in the preceding image, use Shift+Shift, Find ‘Show indent guides’, and enable it in IntelliJ IDEA.
The following image shows another way to understand which leading spaces are included in your text blocks – blue rectangles represent the spaces that are not part of your textblock and the light green rectangles represent the leading spaces that are included in your text block:
If you move the closing triple quotes to the left, the white spaces included in the textblock changes, as shown in the following image:
Trailing white spaces
By default, the trailing white spaces are removed in Text Block values. IntelliJ IDEA can detect when you add trailing white spaces in your textblocks. It would highlight those spaces (to ensure you didn’t add them by mistake).
When you click Alt + Enter, it could prompt you to either ‘Escape trailing whitespace characters’, or ‘Remove trailing whitespace characters’. If you choose the former option, IntelliJ IDEA will add \s at the end (\s represents a single space), as shown in the following gif:
Where would you use a trailing white space?
Imagine you are using a method from a library that reads the first 40 characters of a line to extract two values from it, and store it in a Map
, as follows:
public MapparseFixedWidthData(String fixedWidthData) { Map result = new HashMap<>(); String[] lines = fixedWidthData.split("\n"); for (String line : lines) { String field1 = line.substring(0, 19).trim(); String field2 = line.substring(20, 39).trim(); result.put(field1, field2); } return result; }
If you are using a textblock to pass value to the method parseFixedWidthData
, you should define it as follows, escaping the trailing whitespaces, so the the preceding method doesn’t throw an IndexOutOfBounds
exception:
String fixedWidthData = """ CUSTOMER_NAME JOHN DOE \s ACCOUNT_NUMBER 12345678-9879 \s AGE 45 \s""";
Continuation char – \
When you place your text on a new line in a text block, a new line char is added to your String value. Imagine using a textblock to store a store long URL so that it is easy to read, as follows:
String apiUrl = """ https://www.alamy.com/stock-photo-abstract-geometric-pattern-hipster-fashion-design-print-hexagonal-175905258.html? imageid=0DF26DE9-AC7B-4C78-8770-E1AC9EC8783A &p=379271 &pn=1 &searchId=8cf93ae4926578c6f55e3756c4010a71&searchtype=0""";
However, if you use the preceding text block to connect to a URL and retrieve a response, the code will throw an exception. Inclusion of \n in the URL makes it an invalid URL. To address it, you can use the continuation character, that is, \
at the end of a line in your text block (so that the resulting string doesn’t include a new line character):
String apiUrl = """ https://www.alamy.com/stock-photo-abstract-geometric-pattern-hipster-fashion-design-print-hexagonal-175905258.html?\ imageid=0DF26DE9-AC7B-4C78-8770-E1AC9EC8783A\ &p=379271\ &pn=1\ &searchId=8cf93ae4926578c6f55e3756c4010a71&searchtype=0""";
More about TextBlocks
With the syntax rules under your belt, let’s learn more about Text blocks.
Not a String variation
Java isn’t adding a variation of type String with Text Blocks. They are compiled to regular String instances (java.lang.String
). You can think of Textblocks as syntactic sugar that allows you to write Strings without using the concatenating operators and escape sequences. If you decompile a class that defines a text block, you’ll see that they are compiled to regular strings with single pair of double quotes as the delimiter, as shown in the following gif (the top bar mentions that you are viewing a Decompiled .class file):
Call any String method on a text block
Since there is just one java.lang.String type (not a variation for Text blocks), it means that you can call all String methods on text blocks:
Convert a text block to a regular string
Imagine you are migrating your codebase to a development environment that doesn’t support Textblocks (Java 14 or earlier versions). In such case, you can invoke Context Actions to convert a Text Block to a regular String literal:
Language Injections in Textblocks
Injecting a language into Text Blocks in IntelliJ IDEA enables syntax highlighting and real-time error detection, helping to catch issues such as unclosed JSON values or HTML tags, missing or mismatched quotes in attributes, inconsistent indentation, and unescaped special characters. You also get IntelliJ IDEA’s support like code completion, and value validation.
The following gif shows how you can inject JSON as a language in a text block (language injection in IntelliJ IDEA applies to regular strings too):
As you can see, the language injection option enables you to choose from multiple options (including JSON).
Practical examples – where to use Text Blocks
Apart from using Textblocks to store JSON data (as shown in the preceding sections), you can think of using Text Blocks to store values that usually span multiple lines such as XML, HTML data, or code snippets written in other programming languages. This section highlights the practical examples where you can use text blocks.
1. ASCII Art
You can use textblock to store and output ASCII art, such as the following:
String textblock = """ ╔═══════════════════════════════════════════════════════════════════════════════════════╗ ║ ║ ║ ████████╗███████╗██╗ ██╗████████╗ ██████╗ ██╗ ██████╗ ██████╗██╗ ██╗ ║ ║ ╚══██╔══╝██╔════╝╚██╗██╔╝╚══██╔══╝ ██╔══██╗██║ ██╔═══██╗██╔════╝██║ ██╔╝ ║ ║ ██║ █████╗ ╚███╔╝ ██║ ██████╔╝██║ ██║ ██║██║ █████╔╝ ║ ║ ██║ ██╔══╝ ██╔██╗ ██║ ██╔══██╗██║ ██║ ██║██║ ██╔═██╗ ║ ║ ██║ ███████╗██╔╝ ██╗ ██║ ██████╔╝███████╗╚██████╔╝╚██████╗██║ ██╗ ║ ║ ╚═╝ ╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝ ╚═════╝ ╚═════╝╚═╝ ╚═╝ ║ ║ ║ ╠═══════════════════════════════════════════════════════════════════════════════════════╣""";
2. Logging data
Imagine while working with an online shopping application, you need to log a message with order details, if the quantity for a product in an order is 0 or negative. It is common to create a String that includes literals, such as, ‘Invalid order’, and order details that can be accessed using variables like orderId, etc. Here’s a sample code to accomplish this (focus on the concatenated String):
public void processOrder(int orderId, String product, int qty, LocalDate orderDate) { if (qty <= 0) { String errorMessage = "Invalid order quantity:" + qty + "for product" + product + ",order ID" + orderId; logger.error(errorMessage); return; } //.. Remaining code }
The code seems harmless. However, I’ve often missed adding spaces before and after the literal text values in similar code, generating a log message similar to the following that is hard to read:
Invalid order quantity: -5for productWidget,order ID12345
A safer bet would be to use textblocks for this logging message that can help you spot the missing spaces. Even if you miss adding spaces, the new line characters can space out the log messages:
public void processOrder(int orderId, String product, int qty, LocalDate orderDate) { if (qty <= 0) { String errorMessage = (""" Invalid order quantity:%d for product %s, order ID %d""").formatted(qty, product, orderId); logger.info(errorMessage); System.out.println(errorMessage); return; } //.. Remaining code }
3. XML or HTML data
Here’s an example of a Text Block storing a HTML value:
String html = """Stop generating 6 million tons of plastic waste
4. Complex JSON data
In the beginning of this blog post, I covered how text blocks can help eliminate the clutter. The clutter increases manifolds, when you start working with more complex JSON objects, as follows:
String json = "{\n" + " \"cod\": \"200\",\n" + " \"city\": {\n" + " \"id\": 524901,,,,\n" + " \"name\": \"GreatCity\",\n" + " \"country\": \"AwesomeCountry\",\n" + " \"coord\": {\n" + " \"lat\": 55.7522,\n" + " \"lon\": 37.6156\n" + " }\n" + " }\n" + "}";
With textblocks, the cognitive load reduces, as you can see in the following code snippet:
String json = """ { "cod": "200", "city": { "id": 524901,,,, "name": "GreatCity", "country": "AwesomeCountry", "coord": { "lat": 55.7522, "lon": 37.6156 } } }""";
Perhaps you can inject language in the preceding text block and determine the syntax errors with the JSON value.
5. Multiline String values
Here’s just a long line of String, stored using Text Blocks:
String aLongString = """ I'm a long String value, which can't fit on a Single line. "Hey!", would you prefer a cup of coffee? "Yes, please". """;
Text Blocks take off the visual clutter from multiline strings which existed in the form of concatenation operators and escape sequences.
6. SQL Queries
Imagine using the following code to store a SQL query:
String query = "SELECT name, age" + "FROM EMP" + "WHERE name = \'John\'" + "AND age > 20";
The preceding code represents an invalid query. Due to missing spaces at the end of each line, this query will be interpreted as the following:
SELECT name, ageFROM EMPWHERE name = 'John'AND age > 20 You can address these issues by using text blocks: String query = """ SELECT name, age FROM EMP WHERE name = 'John' AND age > 20 """;
7. Email templates – multiline string values with literal and variable values
When concatenating string literals with variable values, it is easy to miss adding a single space in string literal, right before or after a variable value. It could result in poorly formatted output, or output that is not-so-readable. It could also result in displaying output you didn’t expect due to those missing spaces. Consider the following code that uses a combination of string literals and variable values to send a text to a customer:
String username = "Alice"; String topic = "Java Records"; String previousContext = "We were discussing immutable data classes."; String email = "Hi" + username + ",\n\n" + "Let's continue our discussion about " + topic + ".\n" + "For context, " + previousContext + "\n\n" + "Can you tell me more about what specific aspects of" + topic + "you're interested in?";
You could use TextBlock and formatted(), so that the variable substitution is cleaner:
String email = """ Hi %s, Let's continue our discussion about %s. For context, %s Can you tell me more about what specific aspects of %s you're interested in? """.formatted(username, topic, previousContext, topic);
8. Creating simple bills
You can create simple bills (such as the following) to print using textblocks:
-------------------------------------------------------------------------------------- Your Neighbourhood Art Supplies Store -------------------------------------------------------------------------------------- Date: 2023-10-20 Invoice Number: 12345 Customer Details Name: John Smith Address: 123 Main Street City: Smallville Phone: 555-123-4567 -------------------------------------------------------------------------------------- S.No. Item Name Quantity Unit Price($) Total($) -------------------------------------------------------------------------------------- 1 Acrylic Paint Set 1 20.00 20.00 2 Watercolor Brushes 5 15.00 75.00 3 Sketchbook 12 10.00 120.00 4 Oil Paints Set 1 25.00 25.00 5 Canvas Panels (5-pack) 6 12.00 72.00 -------------------------------------------------------------------------------------- Subtotal: $82.0 Sales Tax (6%): $4.92 Total Amount: $86.92; -------------------------------------------------------------------------------------- Thank you for shopping with us! --------------------------------------------------------------------------------------
Code Migrations – using text blocks instead of a regular string
The release of Java 25, the next LTS version, is around the corner. If you plan to migrate your existing codebases using JDK version 14 or earlier to a newer version, you can start using Text Blocks in your code.
To migrate all eligible multiline String values currently stored across multiple lines using concatenation operators to Text Blocks, you can proceed in two ways. The first approach is to run the inspection “Text blocks can be used” on your entire project or selected directories. In the Problems view window that opens, you can apply these changes individually or in a batch.
To demonstrate this feature, I forked an open-source project from GitHub, JSON-java, and ran the inspection “Text blocks can be used,” as shown in the following GIF:
The second approach is to create a new profile in Settings, say, ‘Migrate to 24’, and add all the migration inspections to this profile. Then, you can execute the ‘Inspect Code…’ command and run this inspection profile on your codebase. Use the Problems view window to accept multiple changes at once or review them individually.
Summary
Text blocks in Java are syntactic sugar to make it easy for you to create string values that span multiple lines, without needing to use concatenation operators or escape sequences. This makes it easier to read and write such values, reducing cognitive load for us developers. Since the values are clutter-free, you can also spot syntax errors in these multiline values, such as a missing quote or comma. By injecting a language or a reference into these text blocks, IntelliJ IDEA can help you further by highlighting these errors and even suggesting how to fix them.
Text blocks start and end with three double quotes. By default, trailing whitespaces are ignored in text blocks. To include—or in other words, escape—the trailing whitespaces, use \s
. To join two lines, add a backslash (\
) at the end of the first line.
Text blocks are quite useful when you’re working with data that usually spans multiple lines, such as JSON, SQL queries, HTML, XML, and others. You could use text blocks to output beautiful line art, format log messages, or even generate simple bills for your neighbourhood stores.
The release of Java 25 is around the corner. If you’re still working with an older version of the JDK, such as 8 or 11, I recommend moving to a newer version so you can benefit from newer features like text blocks.
Happy coding!