Library / SDK
wimdeblauwe/htmx-spring-boot avatar
wimdeblauwe/htmx-spring-boot

htmx-spring-boot: making htmx feel native in a Spring Boot application

Spring Boot and Thymeleaf helpers for working with htmx

690 stars65 forksJavaApache-2.0

At a glance

What is it?
htmx-spring-boot is a Spring Boot starter that gives htmx-aware controllers, argument resolvers for htmx request headers, response header helpers and a Thymeleaf dialect, so an htmx partial swap is a normal annotated controller method rather than a pile of header checks. It suits a Thymeleaf and Spring MVC application, and its version follows htmx rather than Spring Boot.
Who is it for?
htmx-spring-boot fits a Spring MVC and Thymeleaf team that has chosen htmx for partial page updates and wants the server side to understand htmx headers properly, and it does not fit a Spring WebFlux application or a project whose frontend is not htmx.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 5 days ago.
What is it written in?
Mainly Java, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What the starter actually adds to a Spring Boot application

htmx-spring-boot is a library of Spring Boot and Thymeleaf helpers for working with htmx, and the readme describes it as simplifying the integration of htmx with Spring Boot and Spring Web MVC applications.

The specific claim is that it provides a set of views, annotations, and argument resolvers for controllers to easily handle htmx-related request and response headers, which ensures interaction between the frontend and backend, especially for dynamic content updates via htmx. The stated goal is implementing htmx-driven interactions such as AJAX-based partial page updates with minimal configuration.

The second half of the project is a custom Thymeleaf dialect that renders htmx-specific attributes inside Thymeleaf templates, so you write hx-get and hx-post as template attributes rather than as stringly-typed HTML.

The audience is a server-rendered application. That distinction matters more than it sounds, because the whole value of htmx is avoiding a separate frontend application, and a starter that lives in the controller layer is designed for a Thymeleaf or JSP application where the server owns the markup. The repository layout matches that split, with two module directories, htmx-spring-boot/ and htmx-spring-boot-thymeleaf/.

The project is MIT licensed, and the last push was on 2026-10-18, with 6.0.1 the same day and 6.0.0 on 2026-10-08 after 5.2.0 on 2026-10-02.

Two artifacts, and picking the one you actually need

The starter publishes two separate libraries on Maven Central, and the readme is explicit about which is which.

The core one provides annotations and helper classes:

xml
<dependency>
    <groupId>io.github.wimdeblauwe</groupId>
    <artifactId>htmx-spring-boot</artifactId>
    <version>LATEST_VERSION_HERE</version>
</dependency>

The Thymeleaf one provides a Thymeleaf dialect to easily work with htmx attributes:

xml
<dependency>
    <groupId>io.github.wimdeblauwe</groupId>
    <artifactId>htmx-spring-boot-thymeleaf</artifactId>
    <version>LATEST_VERSION_HERE</version>
</dependency>

Both are under the io.github.wimdeblauwe group id, and the readme links the Thymeleaf artifact's Maven Central page, which is where the version list lives.

The split is worth pausing on. A server-rendered application that renders htmx attributes by hand in plain HTML does not need the dialect, and an application that wants the Thymeleaf dialect without the controller helpers would have to take the Thymeleaf module, which presumably brings the core module with it. The two-dependency arrangement is not a licensing or packaging quirk, it is a deliberate choice to let a non-Thymeleaf template engine use the controller side.

There is also a version story to check. The library targets htmx 4.x and later, and for htmx 2.x support the readme points at the 5.x branch. Since htmx's own major version is embedded in the design, an application on htmx 2.x is on a different branch of this project, not on an older release of the same one.

@HxRequest: restricting a controller method to htmx calls

The central feature is the @HxRequest annotation, which selects a controller method only if the request was made by htmx, for example via an hx-get attribute.

java
@HxRequest
@GetMapping("/users")
public String users() {
    return "view";
}

The readme notes that the annotation allows composition, so you can combine annotations to create a custom @HxGetMapping, which is how you would add a specialised composed annotation for your own codebase rather than repeating the combination everywhere.

The refinements are what make it more than a boolean check. You can restrict invocation to a specific triggering element by setting the annotation's value to the ID or name of the element, and if you want to be explicit you use the triggerId or triggerName attributes instead:

java
@HxRequest("my-element")
@GetMapping("/users")
public String users() {
    return "view";
}

And if you want the method to be limited to requests that have a specific target element defined, there is a target attribute:

java
@HxRequest(target = "my-target")
@GetMapping("/users")
public String users() {
    return "view";
}

The practical value is in the error case. A plain @GetMapping for the same path serves both the htmx partial and the full page, and a client that gets the wrong one sees a broken page. With @HxRequest, the mapping is unambiguous, and the target and trigger restrictions mean two different buttons can post to the same URL and reach different handlers.

Reading request headers with HtmxRequest

For inspecting what htmx sent, the library provides the HtmxRequest class as a controller method argument, which gives access to the various htmx request headers.

java
@HxRequest
@GetMapping("/users")
public String users(HtmxRequest htmxRequest) {
    if (htmxRequest.isHistoryRestoreRequest()) {
        // do something
    }
    return "view";
}

The example uses a history restore check, which is the header htmx sets when the browser is restoring a page from its back/forward cache. That is exactly the kind of detail that is easy to get wrong when you read the headers yourself, because a restore request looks like a normal request except for one header.

The argument resolver does the parsing, which is the point of using it. The htmx request headers are a documented set with specific semantics, and the resolver turns them into a typed object with methods, so a controller asks a question like isHistoryRestoreRequest rather than reading a string and comparing it to a literal.

The tradeoff is the usual one with a typed wrapper: you get the headers the library models, and anything it does not expose is not available through the object. The readme does not list the full set of accessors, so the Javadoc link is the reference for what the object actually offers.

Response headers, redirect views, and the JsonMapper that must stay ASCII

Setting htmx response headers has two routes in this library, and the readme explains when to use which.

The first is to take HtmxResponse as a controller method argument and combine it with a view as the return value, for example HtmxRedirectView. The second is to use annotations such as @HxTrigger. The readme is clear that the first method is more flexible and allows you to dynamically set the response headers based on the request, which is what you need for a header whose value depends on what happened server side.

The exception is control flow headers. Most response headers can be set through HtmxResponse, except for some control flow response headers such as HX-Redirect, and for those you have to use a corresponding view as the return value of the controller method. The readme lists HtmxRedirectView, which sets the HX-Redirect header to do a client-side redirect, and HtmxLocationRedirectView, which the excerpt cuts off but is clearly the sibling for the HX-Location header.

One configuration detail is genuinely important and easy to miss. The JsonMapper used to serialize JSON values written to htmx response headers, for example HX-Trigger, can be customised by overriding the bean named htmxJsonMapper:

java
@Bean
JsonMapper htmxJsonMapper() {
    return JsonMapper.builder()
                     // header values must be ASCII, so keep escaping non-ASCII characters
                     .enable(JsonWriteFeature.ESCAPE_NON_ASCII)
                     // your customizations
                     .build();
}

The comment in the example states the reason plainly: header values must be ASCII. HTTP header values are not Unicode-safe across the range of servers, proxies and browsers that will carry them, and a JSON payload with a non-ASCII character in it can arrive mangled. Escaping non-ASCII on the way out is the fix, and the fact that the library exposes the mapper as an overridable bean rather than hard-coding it means you can adjust the serialization when you need to.

What the starter does not do, and where the alternatives sit

Two boundaries are worth naming before you commit.

The first is the rendering model. Everything here is aimed at a server-rendered application with Thymeleaf or a similar template engine, where the server owns the markup and htmx swaps fragments of it. If your frontend is a separate SPA, a library that adds htmx-aware controllers and a Thymeleaf dialect solves a problem you do not have, and the htmx request headers become a protocol detail you would handle in your own client code.

The second is the Spring stack. The readme names Spring Web MVC explicitly. There is no mention of WebFlux, and the argument resolvers and view-based response headers are MVC concepts, so a reactive application would be on its own here.

The alternatives split along the same lines. Doing it by hand means reading the HX-Request header yourself in a Spring MVC controller and setting the response headers on the HttpServletResponse, which is maybe twenty lines of code for a simple case, and which becomes a mess once you have several endpoints and a policy about which of them serve fragments. A hand-rolled Thymeleaf dialect, or simply writing the hx- attributes directly in the template, covers the template side. A different template engine integration, or a different framework, is a different project entirely.

The case for the starter is not that any of this is hard. It is that the htmx request and response headers have enough specific rules, including the history restore case, the target and trigger restrictions, and the ASCII constraint on header payloads, that encoding them once and having them tested is worth more than the alternative of encoding them again in every application. The cost is a dependency, a version line to track, and a small amount of framework coupling.

Editorial conclusion

htmx-spring-boot fits a Spring MVC and Thymeleaf team that has chosen htmx for partial page updates and wants the server side to understand htmx headers properly, and it does not fit a Spring WebFlux application or a project whose frontend is not htmx. Before adopting it, confirm the htmx major version your page uses matches the one this release line targets, since htmx 2.x users are pointed at the 5.x branch, and read the htmxJsonMapper bean if you rely on HX-Trigger headers with non-ASCII values.

Frequently asked questions

What does htmx-spring-boot provide?

It provides a set of views, annotations and argument resolvers for controllers to handle htmx-related request and response headers in Spring Boot and Spring Web MVC applications, plus a custom Thymeleaf dialect for rendering htmx-specific attributes in templates.

How do I add htmx-spring-boot to a Maven project?

Add the io.github.wimdeblauwe group with the htmx-spring-boot artifact for the annotations and helper classes, or htmx-spring-boot-thymeleaf for the Thymeleaf dialect. Both are published on Maven Central, and the version is currently on the 6.x line.

How do I make a controller method run only for htmx requests?

Annotate it with @HxRequest, which selects the method only when the request came from htmx, for example an hx-get. You can narrow it further with the value attribute for an element id or name, the triggerId or triggerName attributes, or the target attribute for a specific target element.

How do I set htmx response headers such as HX-Trigger and HX-Redirect?

Most response headers can be set through HtmxResponse as a controller method argument, and the readme says that approach is more flexible because the values can depend on the request. Control flow headers such as HX-Redirect are the exception and need a corresponding view, such as HtmxRedirectView, as the return value.

Which htmx version does htmx-spring-boot support?

The library targets htmx 4.x and later. For htmx 2.x support the readme directs you to the 5.x branch of the project, so an application on an older htmx major version is on a different branch rather than an older release.

Official sources

  1. Issues
  2. License: Apache-2.0
  3. README
  4. Releases
  5. wimdeblauwe/htmx-spring-boot on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/wimdeblauwe-htmx-spring-boot.svg)](https://hysenlabs.com/projects/wimdeblauwe-htmx-spring-boot)

Was this useful?