1. Introduction

Thymeleaf is a Java template engine for processing and creating HTML, XML, JavaScript, CSS and plain text. For an intro to Thymeleaf and Spring, have a look at this write-up.

In this article, we will discuss new features of Thymeleaf 3.0 in Spring MVC with Thymeleaf application. Version 3 comes with new features and many under-the-hood improvements. To be more specific, we will cover the topics of natural processing and Javascript inlining.

Thymeleaf 3.0 includes three new textual template modes: TEXT, JAVASCRIPT, and CSS – which are meant to be used for processing plain, JavaScript and CSS templates, respectively.

2. Maven Dependencies

First, let us see the configurations required to integrate Thymeleaf with Spring; thymeleaf-spring library is required in our dependencies:

Note that, for a Spring 4 project, the thymeleaf-spring4 library must be used instead of thymeleaf-spring5. The latest version of the dependencies can be found here.

3. Java Thymeleaf Configuration

First, we need to configure new template engine, view and template resolvers. In order to do that, we need to update the Java config class, created

In order to do that, we need to update the Java config class, created here. In addition to new types of resolvers, our templates are implementing Spring interface ApplicationContextAware:

@Configuration
@EnableWebMvc
@ComponentScan({ "com.baeldung.thymeleaf" })
public class WebMVCConfig implements WebMvcConfigurer, ApplicationContextAware {

    private ApplicationContext applicationContext;

    // Java setter

    @Bean
    public ViewResolver htmlViewResolver() {
        ThymeleafViewResolver resolver = new ThymeleafViewResolver();
        resolver.setTemplateEngine(templateEngine(htmlTemplateResolver()));
        resolver.setContentType("text/html");
        resolver.setCharacterEncoding("UTF-8");
        resolver.setViewNames(ArrayUtil.array("*.html"));
        return resolver;
    }
    
    @Bean
    public ViewResolver javascriptViewResolver() {
        ThymeleafViewResolver resolver = new ThymeleafViewResolver();
        resolver.setTemplateEngine(templateEngine(javascriptTemplateResolver()));
        resolver.setContentType("application/javascript");
        resolver.setCharacterEncoding("UTF-8");
        resolver.setViewNames(ArrayUtil.array("*.js"));
        return resolver;
    }
    
    @Bean
    public ViewResolver plainViewResolver() {
        ThymeleafViewResolver resolver = new ThymeleafViewResolver();
        resolver.setTemplateEngine(templateEngine(plainTemplateResolver()));
        resolver.setContentType("text/plain");
        resolver.setCharacterEncoding("UTF-8");
        resolver.setViewNames(ArrayUtil.array("*.txt"));
        return resolver;
    }
}

As we may observe above, we created three different view resolvers – one for HTML views, one for Javascript files, and one for plain text files. Thymeleaf will differentiate them by checking filename extensions – .html, .js and .txt, respectively.

We also created the static ArrayUtil class, in order to use method array() that creates required String[] array with view names.

In the next part of this class, we need to configure template engine:

private ISpringTemplateEngine templateEngine(ITemplateResolver templateResolver) {
    SpringTemplateEngine engine = new SpringTemplateEngine();
    engine.setTemplateResolver(templateResolver);
    return engine;
}

Finally, we need to create three separate template resolvers:

private ITemplateResolver htmlTemplateResolver() {
    SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
    resolver.setApplicationContext(applicationContext);
    resolver.setPrefix("/WEB-INF/views/");
    resolver.setCacheable(false);
    resolver.setTemplateMode(TemplateMode.HTML);
    return resolver;
}
    
private ITemplateResolver javascriptTemplateResolver() {
    SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
    resolver.setApplicationContext(applicationContext);
    resolver.setPrefix("/WEB-INF/js/");
    resolver.setCacheable(false);
    resolver.setTemplateMode(TemplateMode.JAVASCRIPT);
    return resolver;
}
    
private ITemplateResolver plainTemplateResolver() {
    SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
    resolver.setApplicationContext(applicationContext);
    resolver.setPrefix("/WEB-INF/txt/");
    resolver.setCacheable(false);
    resolver.setTemplateMode(TemplateMode.TEXT);
    return resolver;
}

Please note, that for testing it is better to use non-cached templates, that’s why it is recommended to use setCacheable(false) method.

Javascript templates will be stored in /WEB-INF/js/ folder, plain text files in /WEB-INF/txt/ folder, and finally path to HTML files is /WEB-INF/html.

4. Spring Controller Configuration

In order to test our new configuration, we created following Spring controller:

@Controller
public class InliningController {

    @RequestMapping(value = "/html", method = RequestMethod.GET)
    public String getExampleHTML(Model model) {
        model.addAttribute("title", "Baeldung");
        model.addAttribute("description", "Thymeleaf tutorial");
        return "inliningExample.html";
    }
    
    @RequestMapping(value = "/js", method = RequestMethod.GET)
    public String getExampleJS(Model model) {
        model.addAttribute("students", StudentUtils.buildStudents());
        return "studentCheck.js";
    }
    
    @RequestMapping(value = "/plain", method = RequestMethod.GET)
    public String getExamplePlain(Model model) {
        model.addAttribute("username", SecurityContextHolder.getContext()
          .getAuthentication().getName());
        model.addAttribute("students", StudentUtils.buildStudents());
        return "studentsList.txt";
    }
}

In the HTML file example, we’ll show how to use the new inlining feature, with and without escaping HTML tags.

For the JS example we’ll generate an AJAX request, which will load js file with students information. Please note, that we are using simple buildStudents() method inside StudentUtils class, from this article.

Finally, in the plaintext example, we’ll show student information as a text file. A typical example of using the plain text template mode could be used for sending plain-text email.

As an additional feature, we will use SecurityContextHolder, to obtain the logged username.

5. Html/Js/Text Example Files

The last part of this tutorial is to create three different types of files, and test the usage of the new Thymeleaf features. Let’s start with HTML file:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Inlining example</title>
</head>
<body>
    <p>Title of tutorial: [[${title}]]</p>
    <p>Description: [(${description})]</p>
</body>
</html>

In this file we use two different approaches. In order to display title, we use escaped syntax, which will remove all HTML tags, resulting in displaying only text. In the case of description, we use unescaped syntax, to keep HTML tags. The final result will look like this:

<p>Title of tutorial: Baeldung</p>
<p>Description: <strong>Thymeleaf</strong> tutorial</p>

which of course will be parsed by our browser, by displaying word Thymeleaf with a bold style.

Next, we proceed to test out js template features:

var count = [[${students.size()}]];
alert("Number of students in group: " + count);

Attributes in JAVASCRIPT template mode will be JavaScript-unescaped. It will result in creating js alert. We load this alert, by using jQuery AJAX, in the listStudents.html file:

<script>
    $(document).ready(function() {
        $.ajax({
            url : "/spring-thymeleaf/js",
            });
        });
</script>

The last, but not the least function, that we want to test is plain text file generation. We created studentsList.txt file with following content:

Dear [(${username})],

This is the list of our students:
[# th:each="s : ${students}"]
   - [(${s.name})]. ID: [(${s.id})]
[/]
Thanks,
The Baeldung University

Note that, as with the markup template modes, the Standard Dialects include just one processable element ([# …]) and a set of processable attributes (th:text, th:utext, th:if, th:unless, th:each, etc.). The result will be a text file, that we may use for example in the email, as it was mentioned at the end of Section 3.

How to test? Our suggestion is to play with the browser first, then check existing JUnit test as well.

6. Thymeleaf in Spring Boot

Spring Boot provides auto-configuration for Thymeleaf by adding the spring-boot-starter-thymeleaf dependency:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
    <version>2.3.3.RELEASE</version>
</dependency>

No explicit configuration is necessary. By default, HTML files should be placed in the resources/templates location.

7. Conclusion

In this article, we discussed new features implemented in Thymeleaf framework with a focus on Version 3.0.

The full implementation of this tutorial can be found in the GitHub project – this is an Eclipse based project, that is easy to test in every modern Internet browser.

Finally, if you’re planning to migrate a project from Version 2 to this latest version, have a look here at the migration guide. And note that your existing Thymeleaf templates are almost 100% compatible with Thymeleaf 3.0 so you will only have to do a few modifications in your configuration.