Spring Boot – Thymeleaf Example


In this tutorial, we will learn Spring Boot Thymeleaf example with login screens.

About Thymeleaf:

Thymeleaf is a server-side Java template engine for web and standalone applications. It provides a natural templating language for HTML,XHTML and XML, and other types of documents. Thymeleaf’s main goal is to provide a better way to create web applications by combining the power of Java with the simplicity of HTML.

Thymeleaf is designed to be a modern, flexible, and highly extensible template engine that works seamlessly with Spring Framework and other Java-based technologies. It offers features such as:

  • A natural and easy-to-learn syntax that can be used directly in HTML and XML documents.
  • A powerful expression language that can be used to access data and execute operations in Java code.
  • Integration with Spring Framework and other Java-based technologies, including support for Spring Security and Spring WebFlux.
  • Support for internationalization and localization.
  • Easy customization and extension through the use of custom dialects and processors.

Overall, Thymeleaf is a powerful and versatile template engine that can be used to build modern and dynamic web applications with Java.

JSP vs Thymeleaf:

  • JSP is not a template engine, It’s compiled to the servlet and then that servlet will be served as web content
  • Thymeleaf is a template engine which takes the HTML file, parses it and then produces web content which is being served.
  • JSP supports Expression Language,Thymeleaf supports Standard Dialect (The expression language) is much more powerful than JSP Expression Language
  • JSP pages required to compile and run where as Thymeleaf is a natural templating engine and We can live-preview the changes without having to compile, build and run

Spring Thymeleaf Tutorial:

  • Create Spring Thymeleat Project In STS: File -> New -> Spring Starer Project
  • Spring Starter Project configuration
Spring Boot Maven project configuration
  1. Spring Starter Project Dependencies :
    • Thymeleaf
    • Spring web
    • lombok for entities and pojos to avoid getters/setters
    • Spring Data JPA to authenticate with database

Required Spring dependencies:

Please check below end of tutorial for complete pom.xml file

                <dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

Note: I am using bootstrap for look and feel, spring data jpa to authenticate user with DB. In the later part of the tutorial,we will see login with spring security Authentication manager, which is recommended approach.

Project Folder Structure:

ProductUiApplication.java

package com.javasavvy.tutorial;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ProductUiApplication {

	public static void main(String[] args) {
		SpringApplication.run(ProductUiApplication.class, args);
	}

}

LoginController.java

package com.javasavvy.tutorial.controller;

import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.servlet.ModelAndView;

import com.javasavvy.tutorial.dto.LoginRequest;
import com.javasavvy.tutorial.service.UserService;

@Controller
public class LoginController {
	
	@Autowired
	private UserService userService;

	@GetMapping("/")
	public String index(Model model, HttpServletRequest request) {
		request.getParameterMap().forEach((k, v) -> {
			model.addAttribute(k, v.length > 0 ? v[0] : "");
		});
		return "index";
	}
	@GetMapping("/dashboard")
	public String dashboard(Model model, HttpServletRequest request) {
		request.getParameterMap().forEach((k, v) -> {
			model.addAttribute(k, v.length > 0 ? v[0] : "");
		});
		return "dashboard";
	}
	
	@PostMapping("/login")
	public ModelAndView login(LoginRequest request) {
		ModelAndView modelAndView = new ModelAndView();    
		modelAndView.setViewName("dashboard");        
		modelAndView.addObject("name", "[email protected]");
		return modelAndView;    
	}
}

LoginRequest.java

if you are facing issues with lombok then install lombok plugin into STS/eclipse:

package com.javasavvy.tutorial.dto;

import lombok.Data;

@Data
public class LoginRequest {

	private String email;
	private String password;

}

User.java

package com.javasavvy.tutorial.entity;

import java.util.Date;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

import lombok.Data;
import lombok.NoArgsConstructor;

@Table(name="user")
@Entity
@Data
@NoArgsConstructor
public class User {

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(name="user_id")
	private long userId;
	
	@Column(name="email")
	private String email;
	
	@Column(name="password")
	private String password;
	
	@Column(name="created_date")
	private Date createdDate;
}

UserRepository.java

package com.javasavvy.tutorial.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import com.javasavvy.tutorial.entity.User;

@Repository 
public interface UserRepository extends JpaRepository<User, Long> {

	
}

UserService.java

package com.javasavvy.tutorial.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.javasavvy.tutorial.repository.UserRepository;

@Service
public class UserService {
	
	@Autowired
	public UserRepository userRepository;

}

application.yaml

spring:
  datasource:
    driver-class-name: org.postgresql.Driver
    url: jdbc:postgresql://localhost:5432/productdb
    username: postgres
    password: root
    hikari:
      connection-timeout: 30000
      maximum-pool-size: 100
      minimum-idle: 20
  jpa:
    properties:
      hibernate:
        dialect: org.hibernate.dialect.PostgreSQLDialect
  thymeleaf:
    prefix: classpath:/templates/
    suffix: .html

index.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <link rel="shortcut icon" type="image/x-icon" class="favicon" href="/assets/images/favicon.ico">
    <title>Login</title>
    <link th:href="@{/assets/bootstrap/bootstrap.min.css}" rel="stylesheet" />
    <link th:href="@{/assets/styles.css}" rel="stylesheet" />

    <script type="text/javascript" th:src="@{/assets/bootstrap/bootstrap.min.js}"></script>
    <script type="text/javascript" th:src="@{/assets/script.js}"></script>
</head>
<body>
  <div id="app-root">
        <div class="content-wrapper">
            <main class="pb-5">
                <div class="center-form-wrapper">
                    <div class="logo">
                         <span>Login</span>
                    </div>
                    <div class="center-form-card">
                        <p class="mt-4 form-info">Please enter your credentials.</p>
                        <form action="/login" method="POST">
                            <div class="form-floating mb-3">
                                <input type="email" class="form-control" id="email" placeholder="Email" required>
                                <label for="email">Email</label>
                            </div>
                            <div class="form-floating mb-3">
                                <input type="password" class="form-control" id="password" placeholder="Password" required>
                                <label for="password">Password</label>
                            </div>
                            <div class="form-group mb-3">
                                <button type="submit" class="btn btn-primary btn-block">Login</button>
                            </div>
                            <a href="#" 
                               th:href="@{/forgotPassword}">Forgot your password?</a>
                        </form>
                    </div>
                </div>
            </main>
            <footer class="">©&nbsp;2023 Javasavvy, Inc. All rights reserved.</footer>
        </div>
    </div>
</body>
</html>

Dashboard.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <link rel="shortcut icon" type="image/x-icon" class="favicon" href="/assets/images/favicon.ico">
    <title>Dashboard</title>
    <link th:href="@{/assets/bootstrap/bootstrap.min.css}" rel="stylesheet" />
    <link th:href="@{/assets/styles.css}" rel="stylesheet" />
    <script type="text/javascript" th:src="@{/assets/bootstrap/bootstrap.min.js}"></script>
    <script type="text/javascript" th:src="@{/assets/script.js}"></script>
</head>
<body>
    <div id="app-root">
        <div class="content-wrapper">
            <main class="pb-5">
               <span th:text="${name}"></span>
            </main>
            <footer class="">©&nbsp;2023 Javasavvy, Inc. All rights reserved.</footer>
        </div>
    </div>
</body>
</html>

POM.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.7.10</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.javasavvy.tutorial</groupId>
	<artifactId>product-ui</artifactId>
	<version>1.0.0</version>
	<name>product-ui</name>
	<description>Spring Boot Thymeleaf tutorial</description>
	<properties>
		<java.version>1.8</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <version>42.2.10</version>
        </dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
		</dependency>
		<!-- <dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency> -->
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
				<configuration>
					<excludes>
						<exclude>
							<groupId>org.projectlombok</groupId>
							<artifactId>lombok</artifactId>
						</exclude>
					</excludes>
				</configuration>
			</plugin>
		</plugins>
	</build>

</project>