HTMX is a lightweight JavaScript library that allows you to perform AJAX requests with declarative attributes in your HTML. This guide will help you transition from Rails UJS or Hotwire to using HTMX for dynamic interactions.
Add HTMX to your project. You can use a CDN for simplicity:
<script src="https://unpkg.com/htmx.org"></script>
Alternatively, install it via npm or yarn:
npm install htmx.org
Include the HTMX script in your layout file:
<head>
<script src="/path-to-htmx.min.js"></script>
</head>
Update your form to include HTMX attributes. For example:
<%= form_with url: posts_path, method: :post, html: { "hx-post": posts_path, "hx-target": "#response-container" } do |form| %>
<%= form.text_field :title %>
<%= form.submit "Submit" %>
<% end %>
hx-post: Specifies the endpoint for the AJAX request.hx-target: Defines where to insert the server's response.Add a target container in your HTML where the response will be rendered:
<div id="response-container"></div>
For links, you can use HTMX attributes like hx-get:
<%= link_to "Load More", posts_path, data: { "hx-get": posts_path, "hx-target": "#post-list" } %>
<div id="post-list"></div>
HTMX sends an HX-Request header in all its requests. Use this to serve different responses for HTMX vs. regular requests.
In your controller action:
def create
@post = Post.new(post_params)
if @post.save
if request.headers["HX-Request"]
render partial: "posts/post", locals: { post: @post }
else
redirect_to posts_path, notice: "Post created successfully."
end
else
if request.headers["HX-Request"]
render partial: "shared/errors", locals: { resource: @post }, status: :unprocessable_entity
else
render :new
end
end
end
Create the _post.html.erb partial for rendering the new post dynamically:
<div class="post">
<h3><%= post.title %></h3>
</div>
Optionally handle errors with a _errors.html.erb partial:
<ul>
<% resource.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
By default, HTMX swaps the content of the target. Customize this behavior using the hx-swap attribute:
<%= form_with url: posts_path, method: :post, html: { "hx-post": posts_path, "hx-target": "#post-list", "hx-swap": "beforeend" } do |form| %>
<%= form.text_field :title %>
<%= form.submit "Add Post" %>
<% end %>
<div id="post-list"></div>
hx-swap="beforeend" appends the new content to the target.Use hx-vals to send additional data with the request:
<button hx-post="/some-endpoint" hx-vals='{"extra_param": "value"}'>Submit</button>
HTMX provides events for handling responses:
document.body.addEventListener("htmx:afterRequest", function (event) {
console.log("Request completed:", event);
});
Enable verbose logging to troubleshoot HTMX:
htmx.config.logging = true;
HTMX replaces Rails UJS or Hotwire with a simpler, declarative approach. By adding hx- attributes to your HTML and leveraging Rails' respond_to with the HX-Request header, you can build dynamic applications with minimal JavaScript.