Back to all articles
Laravel Insights Feb 5, 2026 โˆ™ 1 min read

Laravel and HTMX: A Practical Integration Guide

Enhance Your Laravel Applications with HTMX for Dynamic and Simplified Web Development

The Laravel and HTMX logos are displayed side-by-side, symbolizing a powerful and efficient integration for web development.

Laravel and HTMX: A Practical Integration Guide

Modern web applications demand dynamic, responsive interfaces. For years, the default solution involved heavy JavaScript frameworks like React or Vue.js. While powerful, these frameworks introduce complexity, build steps, and a significant learning curve. HTMX offers a different path by allowing developers to access modern, AJAX-driven features directly from HTML, simplifying the stack without sacrificing interactivity.

When paired with Laravel, HTMX provides an efficient way to build dynamic applications while keeping the logic firmly on the server. This guide offers a comprehensive walkthrough of integrating HTMX into your Laravel projects. We will cover setup, practical examples, and the advantages and disadvantages of this approach, enabling your team to build sophisticated features with streamlined code.

What is HTMX?

HTMX is a lightweight, dependency-free JavaScript library that extends HTML with attributes for making AJAX requests. Instead of writing complex JavaScript to handle events, send requests, and update the DOM, you can add simple attributes to your HTML elements. HTMX intercepts user actions, communicates with the server, and swaps the returned HTML into the DOM.

This approach allows you to build single-page application (SPA) experiences using the server-side rendering patterns you already know. You write your logic and render your views in Laravel, and HTMX handles the dynamic updates on the client side.

Setting Up HTMX in a Laravel Project

Integrating HTMX into a Laravel application is a straightforward process. You only need to include the library in your main layout file.

Step 1: Install HTMX

While you can install HTMX via npm, the simplest method is to use a CDN. This requires no build steps and is perfect for getting started quickly.

Open your main Blade layout file (e.g., resources/views/layouts/app.blade.php) and add the HTMX script tag before the closing </body> tag.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Laravel with HTMX</title>
    @vite('resources/css/app.css')
</head>
<body>
    <div class="container mx-auto">
        @yield('content')
    </div>

    {{-- Add HTMX Script --}}
    <script src="https://unpkg.com/htmx.org@1.9.10"></script>
</body>
</html>

Step 2: Configure CSRF Protection

Laravel's Cross-Site Request Forgery (CSRF) protection requires that all POST, PUT, PATCH, and DELETE requests include a valid CSRF token. To ensure HTMX requests are not blocked, add a meta tag containing the token to your layout's <head>.

<head>
    {{-- ... other meta tags --}}
    <meta name="csrf-token" content="{{ csrf_token() }}">
    @vite('resources/css/app.css')
</head>

HTMX automatically detects this meta tag and includes the token in the X-CSRF-TOKEN header for all non-GET requests.

Real-World Examples with Laravel and HTMX

Let's explore how to implement common features using HTMX. We will build a simple contact management system with dynamic search, inline editing, and form submissions.

Assume we have a Contact model and a ContactController with standard resourceful routes.

Example 1: Dynamic Search and Filtering

A common requirement is to filter a list of records without a full page reload. With HTMX, this becomes incredibly simple.

The Blade View (resources/views/contacts/index.blade.php)

First, set up the search input and the table.

@extends('layouts.app')

@section('content')
    <h1 class="text-3xl font-bold mb-6">Contacts</h1>

    <div class="mb-4">
        <input type="search"
               name="q"
               class="border p-2 rounded-md w-full"
               placeholder="Search contacts..."
               hx-get="{{ route('contacts.index') }}"
               hx-trigger="keyup changed delay:500ms, search"
               hx-target="#contacts-table"
               hx-replace-url="true">
    </div>

    {{-- The target container for search results --}}
    <div id="contacts-table">
        @include('contacts.partials.table', ['contacts' => $contacts])
    </div>
@endsection
  • hx-get: Specifies the URL to send the GET request to.
  • hx-trigger: Defines the event that triggers the request. Here, it triggers on keyup after a 500ms delay to avoid excessive requests.
  • hx-target: The CSS selector for the element that will be updated with the server's response.
  • hx-replace-url="true": Updates the browser's URL with the query string, making search results bookmarkable.

The Partial View (resources/views/contacts/partials/table.blade.php)

Create a separate Blade partial for the table. This allows us to return just the table content for AJAX requests.

<table class="min-w-full bg-white">
    <thead>
        <tr>
            <th class="py-2 px-4 border-b">Name</th>
            <th class="py-2 px-4 border-b">Email</th>
            <th class="py-2 px-4 border-b">Actions</th>
        </tr>
    </thead>
    <tbody>
        @forelse($contacts as $contact)
            <tr>
                <td class="py-2 px-4 border-b">{{ $contact->name }}</td>
                <td class="py-2 px-4 border-b">{{ $contact->email }}</td>
                <td class="py-2 px-4 border-b">
                    {{-- Action buttons will go here --}}
                </td>
            </tr>
        @empty
            <tr>
                <td colspan="3" class="py-4 px-4 text-center text-gray-500">No contacts found.</td>
            </tr>
        @endforelse
    </tbody>
</table>

The Controller (app/Http/Controllers/ContactController.php)

In the controller, check if the request is an HTMX request. If it is, return only the partial view; otherwise, return the full page.

public function index(Request $request)
{
    $query = Contact::query();

    if ($request->has('q')) {
        $query->where('name', 'like', '%' . $request->q . '%')
              ->orWhere('email', 'like', '%' . $request->q . '%');
    }

    $contacts = $query->latest()->get();

    // Check for an HTMX request
    if ($request->header('HX-Request')) {
        return view('contacts.partials.table', compact('contacts'));
    }

    return view('contacts.index', compact('contacts'));
}

Now, as the user types in the search box, the table updates dynamically without reloading the entire page.

Example 2: Modal-Based Create/Edit Forms

Let's load a form in a modal to create a new contact.

The Button (contacts/index.blade.php)

Add a button to open the create form.

<button
    hx-get="{{ route('contacts.create') }}"
    hx-target="#modal-content"
    hx-swap="innerHTML"
    onclick="document.getElementById('modal').classList.remove('hidden')">
    New Contact
</button>

{{-- Modal structure --}}
<div id="modal" class="fixed inset-0 bg-gray-900 bg-opacity-50 hidden flex items-center justify-center">
    <div id="modal-content" class="bg-white p-8 rounded-lg shadow-xl w-1/3">
        {{-- Form content will be loaded here --}}
    </div>
</div>

The Create View (contacts/create.blade.php)

This view contains the form that will be loaded into the modal.

<form hx-post="{{ route('contacts.store') }}" hx-target="#contacts-table" hx-swap="outerHTML">
    @csrf
    <h2 class="text-2xl font-bold mb-4">New Contact</h2>
    {{-- Form fields --}}
    <div class="mb-4">
        <label for="name">Name</label>
        <input type="text" name="name" class="border p-2 rounded-md w-full">
    </div>
    <div class="mb-4">
        <label for="email">Email</label>
        <input type="email" name="email" class="border p-2 rounded-md w-full">
    </div>
    <button type="submit" class="bg-blue-500 text-white py-2 px-4 rounded">Save</button>
    <button type="button" onclick="document.getElementById('modal').classList.add('hidden')">Cancel</button>
</form>
  • hx-post: Submits the form via a POST request.
  • hx-target: Upon successful submission, the response will target the main contacts table.
  • hx-swap="outerHTML": Replaces the entire #contacts-table div with the updated table returned from the server.

The Controller (store method)

After saving the new contact, the store method should return the updated table partial.

public function store(Request $request)
{
    $validated = $request->validate([
        'name' => 'required|string|max:255',
        'email' => 'required|email|unique:contacts,email',
    ]);

    Contact::create($validated);

    // Return the updated table
    $contacts = Contact::latest()->get();
    return view('contacts.partials.table', compact('contacts'));
}

Advantages of Using HTMX with Laravel

  1. Simplified Stack: You can build dynamic interfaces without a complex JavaScript toolchain. Your entire application logic remains in PHP and Blade.
  2. Faster Development: Eliminating the need to create separate APIs and manage client-side state significantly speeds up the development of CRUD applications.
  3. Progressive Enhancement: HTMX-powered applications are functional even with JavaScript disabled, as they default to standard HTML behavior. This improves accessibility and robustness.
  4. Low Learning Curve: For Laravel developers, HTMX is intuitive. It requires learning a few HTML attributes rather than an entire JavaScript framework.

Disadvantages and Considerations

  1. Not a Full SPA Framework: HTMX is not designed for highly complex, stateful client-side applications (like a photo editor or a real-time collaborative document). For such use cases, a dedicated framework like Vue.js or React is more appropriate.
  2. Potential for "Chatty" Applications: Overuse of HTMX can lead to many small HTTP requests. It is important to design interactions thoughtfully to avoid overwhelming the server.
  3. Server Must Return HTML: The core pattern of HTMX relies on the server returning HTML snippets. This differs from the JSON API approach common with other frontend frameworks.

Conclusion

HTMX offers a compelling alternative for building modern, interactive web applications within a server-side framework like Laravel. It bridges the gap between traditional multi-page applications and complex single-page applications, providing a powerful yet simple toolset for enhancing user experience.

By leveraging HTMX, Laravel developers can deliver rich, dynamic interfaces faster and with less complexity. It empowers teams to focus on server-side logic while still providing the responsive feel that users have come to expect. For a wide range of applications, the combination of Laravel and HTMX is an efficient and highly productive choice.


Related articles

Continue exploring Laravel insights and practical delivery strategies.

Laravel consulting

Need senior Laravel help for this topic?

Let's adapt these practices to your product and deliver the next milestone.