Web Development 5-8 minutes

HTMX with Laravel 13: Interactivity Without JavaScript Using HTML Attributes

Diego Cortés
Diego Cortés
Full Stack Developer & SEO Specialist
Share:
HTMX with Laravel 13: Interactivity Without JavaScript Using HTML Attributes

HTMX weighs about 14 kB, has zero dependencies, and turns any Blade button or link into an AJAX request with nothing more than attributes like hx-get: interactivity without JavaScript that Laravel 13 answers with a plain HTML partial. In this guide you set up HTMX in a Laravel 13 project and build live search, sortable tables, and reload-free actions, step by step.

What HTMX Is and Why It Matters in 2026

HTMX is a JavaScript library that puts interactivity back into HTML: with declarative attributes it gives you AJAX requests, CSS transitions, WebSockets, and Server-Sent Events without writing a line of JavaScript yourself. The browser makes the request, the server returns HTML, and HTMX places that fragment wherever you tell it to. For a Laravel developer, that means the backend stays the single source of truth: you do not duplicate logic in a JavaScript client or keep synchronized state between two worlds.

The Hypermedia Philosophy vs. SPAs

Single-page applications move the logic to the client: the server delivers JSON and a JavaScript framework builds the interface. HTMX defends the opposite approach, hypermedia: the server keeps returning HTML, and interactions are regular requests that replace fragments of the page. It is the same architecture of the classic web, but without the full reload. The result is less code, less state to synchronize, and a minimal learning curve if you already know Blade.

What HTMX 2 Offers and What HTMX 4 Will Bring

HTMX 2.0.0 was released on June 17, 2024: it dropped Internet Explorer support, hardened some defaults, and kept most of the core API unchanged. Since then it has been the stable reference version, with support for the whole ecosystem of hx-* attributes. Version 4 is in active beta with a target release in the summer of 2026: it is a rewrite of the implementation on top of the browser's fetch() API, which simplifies the internals and improves request handling. For what you will build today, HTMX 2 is more than enough and perfectly stable.

Installing HTMX in a Laravel 13 Project

There are two ways to bring HTMX into your Laravel 13 project: the official CDN, ideal for prototypes and small projects, or the Vite integration, recommended when you already use Laravel's bundler for the rest of your assets.

With a CDN in Minutes

The fast path is adding the script tag to the head section of your Blade layout:

<script src="https://unpkg.com/htmx.org@2"></script>

With that single line, HTMX is active across the whole application. It is the perfect option for trying the library without touching your build configuration. For production you can point to a specific version and use the integrity attribute if you want to pin the hash.

With Vite and npm

If you prefer to manage HTMX as a dependency, install it with npm and import it in your JavaScript entry file:

npm install htmx.org
import htmx from "htmx.org";
window.htmx = htmx;

HTMX is then bundled with the rest of your JavaScript and loaded through the @vite Blade directive. This path also lets you combine it with Alpine.js if you ever need some client-side behavior, something that is common in real projects.

Your First Request: hx-get, hx-target, and hx-swap

The basic trio of attributes is hx-get, hx-target, and hx-swap. The first defines the URL the request goes to, the second the element where the response lands, and the third how it is inserted. A view counter button would look like this:

<button hx-get="/posts/1/visits" hx-target="#visits" hx-swap="innerHTML">
    View visits
</button>
<span id="visits">0</span>

Responding with a Partial from the Controller

In the controller you decide what to return based on the request type. For an HTMX request you return a Blade partial; for a normal request, the full view:

public function visits(Post $post)
{
    if ($post->visits < 100) {
        // HTMX request: only the fragment
        return view('posts.visits-partial', compact('post'));
    }

    return view('posts.show', compact('post'));
}

The HX-Request Header and Partial Responses

HTMX sends the HX-Request header on every request, and the official docs recommend responding with partials when it is present. In Laravel you can check it with the request's header method, or use the mauricius/laravel-htmx helper package, which adds utilities such as $request->isHtmxRequest() and shortcuts for HTMX responses. The important part is the pattern: a single route that serves the full view or the fragment depending on who is asking, which keeps your application working without JavaScript.

Live Search Without Reloading the Page

Live search is the classic HTMX-in-Laravel example. A text field that queries the server as you type and shows the results in a list, without reloading the page.

<input type="search" name="q"
       hx-get="/search"
       hx-trigger="input changed delay:300ms"
       hx-target="#results"
       hx-indicator="#searching">
<div id="results"></div>
<div id="searching" class="htmx-indicator">Searching...</div>

hx-trigger with Debounce

The hx-trigger attribute with the input changed delay:300ms syntax fires the request 300 milliseconds after the user stops typing. Without that delay, every keystroke would trigger a database query. The controller receives the term and returns the partial with the results:

public function search(Request $request)
{
    $posts = Post::where('title', 'like', "%{$request->q}%")->limit(10)->get();

    return view('posts.results', compact('posts'));
}

Loading Indicators with hx-indicator

The hx-indicator attribute points to an element that HTMX shows automatically while the request is in flight. For it to work, that element needs the htmx-indicator class, which hides it by default and makes it visible during an active request. It is a declarative way to give loading feedback without managing waiting states in JavaScript.

Sortable and Paginated Tables with HTMX

A posts table with column sorting and pagination is another case where HTMX shines: you reuse the same partial view for every table variant.

Sorting Columns by Reusing the Same Partial View

<table>
    <thead>
        <tr>
            <th><a hx-get="/posts?sort=title" hx-target="#posts-table" hx-swap="outerHTML">Title</a></th>
            <th><a hx-get="/posts?sort=date" hx-target="#posts-table" hx-swap="outerHTML">Date</a></th>
        </tr>
    </thead>
    <tbody id="posts-table">
        @include('posts.table-body', ['posts' => $posts])
    </tbody>
</table>

The controller orders the query based on the received parameter and returns the same table-body partial. Since the target is the tbody itself and the swap is outerHTML, the whole table updates without a reload. The key is that the partial only contains the rows, so it works both for the initial load and for each re-sort.

Pagination with hx-boost and pushState

For pagination, each page link uses hx-get pointing at the same table. Adding hx-push-url="true" to the request makes HTMX update the browser URL through the history API, so the back button works and the page is shareable. The next-page link is built with Laravel's pagination helpers, but instead of a full reload, only the table content is replaced.

Actions Without Reload: The Like Button

One-off actions, like liking a post, are the simplest HTMX case: a POST request that updates only the affected counter.

hx-post and Selective Counter Updates

<button hx-post="/posts/1/like" hx-target="#like-count" hx-swap="innerHTML">
    Like
</button>
<span id="like-count">{{ $post->likes }}</span>
public function like(Post $post)
{
    $post->increment('likes');

    return $post->likes;
}

The controller increments the counter and returns the number, which HTMX places inside the span. Notice that the response can be plain text: HTMX does not require HTML when all you want to replace is a simple value. With the HX-Request header you can also tell this request apart and skip rendering the full layout.

HTMX 2 vs Livewire 4: When to Choose Each

Livewire 4 and HTMX solve similar problems with different philosophies. Livewire offers server-driven components with state: each component manages its own lifecycle, properties, and events, and the framework handles the updates. HTMX is more primitive: attributes that fire requests and partials that get inserted, with no state or components. For small and medium interfaces with one-off interactions, HTMX usually suffices and keeps the architecture simple. For complex UIs with lots of shared state, multi-step forms, or dense admin panels, Livewire saves you a fair amount of wiring. The good news is that they are not mutually exclusive: some projects use Livewire for the panels and HTMX for the public-facing areas.

What's Next: htmx 4 and the fetch() API

Version 4 of htmx, in active beta with a target release in the summer of 2026, rewrites the implementation from scratch on top of the browser's fetch() API. This removes internal dependencies, simplifies error handling, and opens the door to modern features such as response streams. For someone starting with HTMX 2 today, the migration is designed to be mostly transparent, since the attributes and the philosophy remain. If you want to try the new stuff, the version 4 site documents the differences, but for production the safe bet is still the stable 2.x branch.

Conclusion

HTMX with Laravel 13 gives you interactivity without changing your architecture: attributes in Blade, partials from the controller, and zero JavaScript to maintain. Start with the CDN, build the live search and the sortable table, and decide between HTMX and Livewire based on the complexity of each interface. If you are into the Laravel ecosystem, this blog also has a full Livewire 4 guide to compare both paths in detail, plus articles on Reverb and WebSockets for when you need real-time updates. Keep reading the blog for more hands-on web development tutorials.

Categories