Skip to main content

Command Palette

Search for a command to run...

The best way to build Spring Boot applications with htmx

Supercharging Hypermedia Driven Application with Spring ViewComponent

Updated
The best way to build Spring Boot applications with htmx
T

Thomas Schühly’s server-side rendering journey started as a developer trying to make life easier while developing his first bootstrapped product in his free time. Creating Spring ViewComponent enabled him to be the youngest Speaker at the largest European Spring conference and build awesome software full-time with his open-source library at alanda.io. He regularly talks at Java User Groups about htmx and server-side rendering with Spring while contributing to the open-source community. PhotoQuest

Why should you even care?

Spring Boot developers usually build JSON APIs that get client-side rendered on the browser.

Marcus Hellberg tells us why we should aim to build a full-stack team:

But with the rise in complexity in modern JavaScript frameworks and the constant change it's hard to keep up!

The browser can only display HTML! Why send JSON to the client that needs to be rendered to HTML? We can just remove this unnecessary complexity!

We can render data directly into a HTML template on the server with an engine like JTE. We then return HTML to the browser directly.

But we lost the interactivity of the JavaScript framework. We can return it by leveraging Hypermedia.

Hypermedia Driven Applications

The core of a hypermedia driven application is hypertext. HTML defines how the application looks and how the user can interact with the application based on the current state of the application.

The underlying principle is "Hypermedia as Engine of Application State".

A simple example is an embedded form that returns a success message on successful submission:

We want a great user experience with our server-side rendered web application.

htmx

The htmx library enables modern UX that is comparable to a single page application.
htmx completes HTML as a hypertext and brings it to the 21st century.

The harsh reality is that HTML never got to the point that it could, by itself, offer UX approaching the thick client. Pure server-side rendered HTML offered only a simple & clunky Click → Request → Page Render model. For reasons I can’t completely understand, HTML never moved beyond this extremely basic UX model.

https://intercoolerjs.org/2020/01/14/taking-html-seriously

htmx enables interactivity by swapping parts of the DOM without reloading the page. It embeds hx attributes (application control information) in the HTML.

If you want to get an introduction to HTMX + Spring Boot:
Interactive web applications with htmx and Spring Boot

UI Components

Spring Boot is an awesome framework for building applications.
However, the traditional Spring MVC style lacks cohesion between the View (Templates) and the Model (Data).

The JavaScript frontend frameworks like React, Angular and Vue share one trait.
They build user interfaces with components. Components enable reusability and define a clear API.

In the Ruby and PHP communities, server-rendered UI components established themselves with Laravel Livewire and Rails ViewComponent

The main benefit is single responsibility:

ViewComponents consolidate the logic needed for a template into a single class, resulting in a cohesive object that is easy to understand

https://viewcomponent.org/#single-responsibility

With the open-source Spring ViewComponent library Spring Developers can enjoy the benefits of UI components.

Introducing Spring ViewComponent

A ViewComponent is a Spring managed bean. We can leverage backend patterns for UI composition.

We create a ViewComponent by annotating a class with the @ViewComponent annotation.

A ViewComponent class defines a rendering context for a corresponding template, this context is called ViewContext. We define a public nested record that implements the ViewContext interface.

SimpleViewComponent.java

@ViewComponent
public class SimpleViewComponent {
    public record SimpleViewContext(String helloWorld) implements ViewContext {
    }

    public SimpleView render() {
        return new SimpleView("Hello World");
    }
}

A ViewComponent needs to have a template with the same name defined in the same package.
In the template, we can access the fields of the ViewContext record.

SimpleViewComponent.jte

@param SimpleViewContext simpleViewContext
<p>
${simpleViewContext.helloWorld()}
</p>
💡
Spring ViewComponent wraps the underlying MVC model using Spring AOP. Technical Implementation

To render the ViewComponent we inject it in the Controller and then call the render method in the endpoint mapping and return the ViewContext result:

@Controller
public class SimpleController {

  private final SimpleViewComponent simpleViewComponent;

  public TestController(SimpleViewComponent simpleViewComponent) {
    this.simpleViewComponent = simpleViewComponent;
  }

  @GetMapping("/")
  ViewContext simple() {
    return simpleViewComponent.render();
  }
}

Perfect fit for HATEOAS

Spring ViewComponent is a perfect fit for building hypermedia-driven web applications.

When the user interacts with the application the state of the application on the server changes.
We now need to send HTML back to swap it into the DOM to reflect the changes in the UI.

With server-side rendered ViewComponents we can call the render method of a ViewComponent again and swap out that component.

Example Application

To explain the benefits I will show you a simple user management application, where we can edit and create users.

If you want to build along, a more detailed guide can be found in my workshop:
Building server-side web applications with htmx

You can play around with the application at htmx.tschuehly.de. Open the DevTools and look at what requests are happening when you interact with the application!

When we want to create/edit a user a modal pops up. The modal is closed when we save the user.

A new table row is automatically appended to the top of the table when we save a new user. When we edit and save an existing user the table row is updated.

This is where Spring ViewComponent comes in. Each part of the UI is a ViewComponent.

We have a UserTableComponent, a UserRowComponent, an EditUserFormComponent and a CreateUserFormComponent.

When we click on the Create new User button htmx will create an HTTP GET request to the /create-user/modal endpoint.

<!-- UserTableComponent.jte -->
<button hx-get="${GET_CREATE_USER_MODAL}" 
        hx-target="#${MODAL_CONTAINER_ID}">
  Create new User
</button>

This will return the HTML of the CreateUserFormComponent that will be rendered in the element with the id: MODAL_CONTAINER_ID

// UserController.java
public static final String GET_CREATE_USER_MODAL = "/create-user/modal";
@GetMapping(GET_CREATE_USER_MODAL)
public ViewContext getCreateUserModal() {
  return createUserComponent.render();
}
💡
I recommend to use constants for the URL endpoints as you can navigate between the controller and the usages with ctrl + b in IntelliJ

When we click on Save User an HTTP POST request is created to the /create-user endpoint.

// UserController.java
public static final String POST_CREATE_USER = "/create-user";
@PostMapping(POST_CREATE_USER)
public ViewContext createUser(String username, String password) {
  EasyUser user = userService.createUser(username, password);
  return userRowComponent.renderNewRow(user);
}

This will first create a new user. Then it will call the UserRowComponent.renderUserRow with the updated user.

// UserRowComponent.java
public ViewContext renderNewRow(EasyUser user) {
  HtmxUtil.retargetId(UserTableComponent.USER_TABLE_BODY_ID);
  HtmxUtil.reswap(HxSwapType.AFTER_BEGIN);
  HtmxUtil.trigger(UserManagementComponent.CLOSE_MODAL_EVENT);
  return new UserRowContext(user);
}

Normally with htmx you define the target, swap and trigger behaviour with hx-attributes. I prefer defining them with HX- reponse headers in the component method. This decouples the source template from the target component.

Here we tell htmx to target the UserTableComponent body, insert the response before the first child and then trigger the CLOSE_MODAL_EVENT

💡
As you can see we use public constants as API to access the internal behaviour of the respective ViewComponent
<!-- UserRowComponent.jte -->
!{var uuid = userRowContext.easyUser().uuid;} 
<tr id="${UserRowContext.htmlUserId(uuid)}">
    <td>
        ${uuid.toString()}
    </td>
    <td>
        ${userRowContext.easyUser().username}
    </td>
    <td>
        ${userRowContext.easyUser().password}
    </td>
    <td>
        <button hx-get="${URI(UserController.GET_EDIT_USER_MODAL,uuid)}"
                hx-target="#${MODAL_CONTAINER_ID}">
            <img src="/edit.svg">
        </button>
    </td>
</tr>

The UserRowComponent.jte has an edit button that calls the GET_EDIT_USER_MODAL endpoint with the uuid of the user as parameter.

After editing a user we need to swap the row of the user we just changed. We can create a rerender method and call it from the corresponding endpoint.

// UserRowComponent.java
public ViewContext rerender(EasyUser easyUser) {
  String target = HtmxUtil.target(UserRowContext.htmlUserId(easyUser.uuid)); 
  HtmxUtil.retarget(target); 
  HtmxUtil.reswap(HxSwapType.OUTER_HTML);
  HtmxUtil.trigger(UserManagementComponent.CLOSE_MODAL_EVENT);
  return new UserRowContext(easyUser); 
}

Advanced Patterns

With Spring ViewComponent we can leverage Dependency Injection to compose the UI while adhering to the open–closed principle.

Another interesting topic is declarative UI with the Strategy Pattern. I have an example repository here: github.com/tschuehly/svc-ui

Lastly we can create higher level abstractions on top of the simple concept of swapping Elements in the DOM that are specific to our domain and use cases.

I will create more blog posts in the future showing the patterns we use at alanda.io to create an enterprise application with Spring ViewComponent and htmx.

If you want to learn more about HTMX + Spring Boot check out my series Web development without the JavaScript headache with Spring + HTMX.

My side business PhotoQuest is also built with HTMX + JTE

J

In March 2024, I found myself facing a bad scenario that many in the cryptocurrency space dread – falling victim to a phishing scam and losing a substantial amount of Bitcoin, totaling around $300,000. It was a devastating blow, one that left me feeling helpless and betrayed by the very technology I had come to trust. However, amidst the despair, a glimmer of hope emerged in the form of Digital Hack Recovery. In the attack, I was determined to learn from my mistake and take proactive measures to prevent such incidents from happening again. I delved into the world of cryptocurrency security, absorbing every piece of information I could find to arm myself against future threats. It was during this research phase that I came across Digital Hack Recovery, a name that would soon become synonymous with salvation. What struck me initially about Digital Hack Recovery was their emphasis on education and awareness. They understood that ignorance was often the greatest vulnerability in the crypto space and sought to empower their clients with the knowledge to mitigate risks effectively. Their website was a treasure trove of resources, offering comprehensive guides on security best practices, common scams to watch out for, and steps to take in the event of a breach. It was evident that they were not just a recovery service but a beacon of guidance in a sea of uncertainty. Upon reaching out to Digital Hack Recovery, I was met with a level of expertise that immediately put me at ease. Unlike other recovery platforms I had encountered, they took the time to assess my case thoroughly before committing to any action. Their transparency was refreshing – they made it clear from the outset what the likelihood of success was and what steps would be involved in the recovery process. This level of honesty instilled confidence in their capabilities and gave me hope that all was not lost. Throughout the recovery journey, Digital Hack Recovery maintained clear and open communication, providing regular updates on their progress and patiently answering any questions or concerns I had along the way. Their dedication to customer satisfaction was evident in every interaction, and it was clear that they genuinely cared about restoring not just my funds but also my peace of mind. In the end, Digital Hack Recovery delivered on its promise, managing to recover approximately 80% of the funds I had lost to the scam. While the financial aspect was certainly significant, it was the sense of closure and justice that proved to be the most valuable takeaway. Thanks to their expertise and perseverance, I was able to reclaim a portion of what was rightfully mine and move forward with renewed confidence in the crypto landscape. I cannot recommend Digital Hack Recovery highly enough to anyone who finds themselves in a similar predicament. Their commitment to education, transparency, and customer satisfaction sets them apart as a beacon of hope in an otherwise tumultuous industry. While the scars of my ordeal may never fully heal, knowing that there are professionals like Digital Hack Recovery standing ready to assist brings a sense of comfort and reassurance that is truly priceless. Trusting them with my recovery was undoubtedly one of the best decisions I have ever made, and I am eternally grateful for their unwavering support. Their contact;

WhatsApp +19152151930

Email; digital hack recovery @ techie . com

More from this blog

Thomas Schilling | Spring/HTMX/Claude Code

22 posts

Youngest Speaker @Spring I/O & Spring ViewComponent creator.

Passionate about building awesome software with Spring + HTMX. Pushing full-stack development with Spring forward.