Streamlining Java Web Apps: Uniting Spring Boot with HTMX, Thymeleaf, and Tailwind for Modern Frontend Solutions
Create modern web apps with tailwind and spring boot by using HTMX
If you’re a Java developer feeling overwhelmed by modern frontend frameworks like React, there’s a simpler approach to web development that doesn’t require mastering these complex tools. Drawing inspiration from Josh Long’s video, this article aims to simplify web development for Java enthusiasts. We’ll explore how Java and Spring Boot can be used to create web apps, aligning more closely with Java developers’ existing skills, thus avoiding the need to learn a new language or framework.
We’ll focus on tools such as HTMX and Thymeleaf, and use Tailwind CSS for styling. HTMX allows Java developers to craft dynamic web pages without deep frontend expertise. Thymeleaf streamlines HTML file management with its templating capabilities. And Tailwind CSS facilitates effortless site styling. This guide is for Java developers looking to build web applications without delving into technologies like React. We’ll leverage familiar tools and introduce straightforward technologies to create outstanding web applications.
Understanding the technologies
HTMX
HTMX is a tool that helps you make websites without needing much JavaScript. It’s extending what you can do with HTML. At the core of HTMX is the possibility to issue AJAX requests directly from HTML.
Think about a normal link in HTML:
<a href="/blog">Blog</a>
When you click this, it just takes you to the “/blog” page. Simple, right?
Now, see what HTMX can do with a button:
<button hx-post="/clicked" hx-trigger="click" hx-target="#parent-div" hx-swap="outerHTML">
Click Me!
</button>
When you click it, it sends a POST request to ‘/clicked’. Whatever answer it gets back, it puts that into the part of your page marked as ‘parent-div’. In HTMX, events can trigger AJAX requests. For example, input
, textarea
, and select
elements trigger on change, forms trigger on submit, and others trigger on click. You can customize this using the hx-trigger
attribute. Additionally, HTMX offers modifiers like once
, changed
, delay
, throttle
, and from
to fine-tune how and when these events trigger requests.
So, HTMX lets you:
Use any part of your HTML to send messages (not just buttons or forms).
Respond to different actions, like clicks or mouse-overs.
Choose different ways to send messages, more than just the usual ways.
Update just a part of your page with new info, without reloading everything.
When using HTMX, your server talks back in HTML and not JSON, keeping the data format simple and consolidated.
And if you like to keep HTML looking standard, HTMX works with that too. You can write like this:
<a data-hx-post="/click">Click Me!</a>
Tailwind
I used to find CSS overwhelming, but then I discovered Tailwind CSS, which made things much easier. Styling something like a button is simple with Tailwind, and it avoids the need for a lot of CSS coding.
<button class="bg-blue-500 text-white font-bold py-2 px-4 rounded">
Click me
</button>
This code segment efficiently applies styles using Tailwind CSS, where classes such as bg-blue-500
for a blue background are directly embedded in the HTML. This approach simplifies styling without requiring extensive CSS expertise. In essence, Tailwind CSS offers:
Rapid, pre-defined styles, significantly save time.
The versatility to combine styles for unique appearances.
Maintenance of a clean and legible HTML structure.
Thymeleaf
If you’ve needed to send dynamic emails from a backend using HTML and Java, you’re likely familiar with Thymeleaf, a Java-based tool that simplifies creating HTML templates. Thymeleaf allows you to design your templates in HTML and then dynamically populates them with data from your Java code. For instance, the line <p th:text="${message}">Default message</p>
instructs Thymeleaf to replace "Default message" with the content of 'message' from your Java backend, ensuring a simple process within the Java ecosystem.
Combining Thymeleaf’s server-side HTML rendering capabilities with HTMX for responsive front-end interactions, you can create a smooth user experience. This combination is effective for handling dynamic content, whether in complex web pages or detailed email templates.
Spring Boot
Spring Boot, coupled with Spring Boot Web, provides a robust framework for building web applications swiftly and efficiently. It offers static web content and allows developers to serve HTML, CSS, and JavaScript files directly and seamlessly. Besides that, we can create the web API we will use for our frontend quite easy.
Developing the Application
We start by generating a new Spring Boot project and we include the following dependencies:
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'io.github.wimdeblauwe:htmx-spring-boot-thymeleaf:2.0.1'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
runtimeOnly 'com.h2database:h2'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
We have integrated this io.github.wimdeblauwe:htmx-spring-boot-thymeleaf
dependency as it offers a convenient return type for handling Out-of-Band Swaps (OOB) which are supported by HTMX. This means HTMX enables us to update multiple parts of a webpage in response to a single AJAX request. This feature allows developers to specify areas of a page to be updated with new content without refreshing the entire page, leading to a better user experience. It’s particularly useful for dynamically updating web content based on user interactions or real-time data changes, enhancing the responsiveness and interactivity of web applications.
Afterward, we craft our template:
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<title>My To-Do List</title>
<!-- do not do it like this in production please! -->
<script src="https://unpkg.com/htmx.org@1.9.3"></script>
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
</head>
We use tailwind via link and HTMX via script, this is not the advised solution, for production please download HTMX or use a different installation method.
<div class="container mx-auto">
<div class="todos-list">
<div th:each="todo: ${todos}" th:fragment="todos" class="todo grid grid-cols-1 md:grid-cols-3 items-center border-t border-gray-300 py-2">
This snippet renders all our todos via Thymeleaf dynamically into the DOM and styling is done via tailwind. If we check the corresponding controller method we can see how we inject the todos by adding them to the model.
@GetMapping
public String getTodos(Model model) {
model.addAttribute("todos", todoRepository.findAll());
return "todos";
}
We include a delete button into each todo we have and want the list updated after we issue a delete request.
<button hx-confirm="Are you sure?"
hx-target="closest .todo"
hx-swap="outerHTML"
class="bg-red-500 text-white py-1 px-2 rounded-full hover:bg-red-600"
hx-trigger="click"
th:attr="hx-delete=@{/todos/{id}(id=${todo.id})}">
Delete
</button>
@ResponseBody
@DeleteMapping(path = "/{todoId}", produces = MediaType.TEXT_HTML_VALUE)
String delete(@PathVariable Integer todoId) {
todoRepository.findById(todoId).ifPresent(todoRepository::delete);
return "";
}
In this setup, the Spring Boot method is designed to handle DELETE requests, deleting a ‘todo’ item based on its ID and responding with an empty string. The HTMX-enabled HTML button, with its hx-target
attribute, targets a specific part of the web page to update. This mechanism, coupled with the empty response from the server, effectively removes the deleted item from the display without the need for a full page refresh.
We then have a different part of the application which is responsible for adding new todos to the list. Here is we the previously discussed hx-swap-oob comes into play, as we have to update our form but also the list.
<div id="todos-form" th:fragment="todos-form" hx-swap-oob="true" class="todos-form mt-4">
<div class="todo line grid grid-cols-1 md:grid-cols-3 items-center border-t border-gray-300 py-2">
<div></div>
<div class="mb-2 mr-2 md:mb-0">
<input type="text" name="new-todo" id="new-todo" class="border p-2 w-full mx-auto focus:outline-none focus:border-blue-500 focus:ring focus:ring-blue-200"/>
</div>
<div class="mt-2 md:mt-0 text-right md:text-left">
<button hx-include="#new-todo"
hx-post="/todos"
hx-target=".todos-list"
hx-trigger="click"
class="bg-blue-500 text-white py-2 px-4 rounded hover:bg-blue-600">
Add
</button>
</div>
</div>
</div>
@PostMapping
HtmxResponse add(@RequestParam("new-todo") String newTodo, Model model) {
todoRepository.save(new Todo(null, newTodo));
model.addAttribute("todos", todoRepository.findAll());
return new HtmxResponse()
.addTemplate("todos :: todos")
.addTemplate("todos :: todos-form");
}
This code demonstrates a dynamic way to update a ‘todos’ list and its corresponding form on a web page using HTMX and Spring Boot.
The HTML part contains a form within a div
element, where users can input a new 'todo' item. The hx-post
attribute on the 'Add' button is set up to send the input data to the server when clicked, targeting the .todos-list
element for updating.
On the server side, the @PostMapping
method in the Spring Boot application handles this data. It saves the new 'todo' item and updates the list of 'todos'. Then, it returns a response that includes two partial HTML fragments (todos :: todos
and todos :: todos-form
) using Thymeleaf templates. This response causes HTMX to update both the 'todos' list and the input form on the page, ensuring that both the list and the form are refreshed with the latest data without a full page reload.
Furthermore, we have a reset button that acts similar to the delete button but targets the whole list instead of a single item.
<div class="mt-4">
<button hx-post="/todos/reset"
hx-target=".todos-list"
class="bg-gray-300 text-gray-700 py-2 px-4 rounded hover:bg-gray-400">
Reset All
</button>
</div>
@PostMapping("/reset")
String reset(Model model) {
eventPublisher.publishEvent(new TodosResetEvent());
model.addAttribute("todos", todoRepository.findAll());
return "todos :: todos";
}
The endpoint creates an event that deletes all current repository entries, creates the new ones, and updates the HTML fragment.
See the whole code on Github.
Conclusion
HTMX’s recognition in the 2023 JavaScript Rising Stars ranking just after React, highlights its growing popularity. It stands out for making web development easier.
HTMX is user-friendly and ideal for those less familiar with JavaScript, like Java or Go developers starting in frontend. It allows adding features like AJAX and WebSockets directly to HTML and reduces code size by up to 67% compared to React. HTMX simplifies web development, making it more accessible and enjoyable.
Have you tried HTMX? What are your thoughts on its approach to enhancing HTML with modern features, and how might this change our view of frontend development?
Connect with me on LinkedIn.