ITSkillsCenter
Développement Web

HTMX + Laravel : CRUD dynamique sans JavaScript — tutoriel 2026

15 min de lecture

Méta-description : Combine HTMX et Laravel 11 pour construire des interfaces CRUD dynamiques, modales, search live et validation inline sans framework JavaScript lourd. Tutoriel pas-à-pas 2026 avec exemples production-ready, déploiement Hetzner CX22 et adaptations contexte ouest-africain.

Pourquoi HTMX + Laravel est le choix pragmatique en 2026

Pendant des années, ajouter du dynamisme à une application Laravel signifiait ouvrir un projet Vue.js ou React parallèle, configurer Vite, gérer une API JSON, gérer un router côté client, gérer un state management. Pour un simple CRUD avec validation inline et recherche en direct, on multipliait par 3 la complexité du projet. HTMX a inversé l’équation. Cette bibliothèque JavaScript minimaliste de 14 Ko ajoute 5 nouveaux attributs HTML (hx-get, hx-post, hx-target, hx-swap, hx-trigger) qui transforment n’importe quelle balise HTML en élément interactif. Le serveur Laravel renvoie des fragments HTML, HTMX les insère au bon endroit du DOM, et la magie opère.

Pour une PME francophone basée à Dakar, Abidjan, Bamako, Ouagadougou ou Conakry, c’est l’option idéale. Tu gardes ton équipe PHP/Laravel, tu n’introduis pas de pipeline de build JavaScript complexe, tu obtiens une UX moderne avec validation en direct, recherche live, modales et infinite scroll. Tout ça en moins de 200 lignes de code par fonctionnalité, déployable sur un Hetzner CX22 à 2 700 F CFA par mois.

Ce tutoriel s’inscrit dans le guide général Web 2026 sans framework lourd : HTMX, Hotwire, Alpine.js. Tu vas y construire un CRUD complet de gestion de clients pour une PME, avec recherche en direct, modale d’édition inline, validation côté serveur affichée instantanément, et pagination dynamique.

Prérequis

  • VPS Hetzner Cloud CX22 (2 700 F CFA/mois) ou environnement local.
  • PHP 8.3+ avec les extensions classiques (mbstring, openssl, pdo_mysql ou pdo_pgsql).
  • Composer 2.7+.
  • Laravel 11+ (le tutoriel cible Laravel 11.20).
  • MySQL 8 ou PostgreSQL 16.
  • Connaissances de base Laravel : routes, contrôleurs, Eloquent, Blade.
  • Aucun prérequis Vue.js ou React. C’est tout l’intérêt.

La philosophie HTMX en 5 attributs

Avant d’écrire le moindre code Laravel, il faut comprendre les 5 attributs HTMX qui couvrent 95 % des cas. C’est étonnamment peu, et c’est précisément ce qui rend HTMX accessible :

  1. hx-get / hx-post / hx-put / hx-delete — déclenche une requête HTTP de la méthode correspondante quand l’événement se produit.
  2. hx-target — sélecteur CSS de l’élément qui recevra la réponse HTML. Par défaut, c’est l’élément lui-même.
  3. hx-swap — comment intégrer la réponse : innerHTML (défaut), outerHTML, beforeend, afterbegin, delete.
  4. hx-trigger — quel événement déclenche la requête : click (défaut sur boutons), submit (défaut sur formulaires), change, keyup delay:300ms pour les debounce.
  5. hx-indicator — sélecteur d’un élément à afficher pendant la requête (spinner de chargement).

Avec ces 5 attributs, tu peux construire des interfaces extrêmement riches sans une ligne de JavaScript. Voyons-le concrètement avec un cas réel : un CRUD clients pour une PME.

Installation et setup

# Sur le VPS ou en local
composer create-project laravel/laravel pme-clients
cd pme-clients

# Configure la base PostgreSQL
sed -i 's/DB_CONNECTION=sqlite/DB_CONNECTION=pgsql/' .env
# ... édite les autres DB_*

php artisan migrate

Pour HTMX, deux options : CDN (rapide, parfait pour démarrer) ou installation locale via NPM (recommandé en production). Optons pour le CDN dans resources/views/layouts/app.blade.php :

<!DOCTYPE html>
<html lang="fr">
<head>
    <meta charset="UTF-8">
    <title>Gestion clients PME</title>
    <script src="https://unpkg.com/htmx.org@2.0.3"></script>
    <script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="bg-gray-50">
    @yield('content')
</body>
</html>

Modèle Client et migration

php artisan make:model Client -mc

Édite la migration générée :

Schema::create('clients', function (Blueprint $table) {
    $table->id();
    $table->string('nom');
    $table->string('email')->unique();
    $table->string('telephone')->nullable();
    $table->string('ville');
    $table->string('pays')->default('Sénégal');
    $table->timestamps();
});
php artisan migrate
php artisan db:seed

Page liste avec search live

Le routeur routes/web.php :

use App\Http\Controllers\ClientController;
Route::get('/clients', [ClientController::class, 'index'])->name('clients.index');
Route::get('/clients/search', [ClientController::class, 'search'])->name('clients.search');
Route::post('/clients', [ClientController::class, 'store'])->name('clients.store');
Route::put('/clients/{client}', [ClientController::class, 'update'])->name('clients.update');
Route::delete('/clients/{client}', [ClientController::class, 'destroy'])->name('clients.destroy');
Route::get('/clients/{client}/edit', [ClientController::class, 'edit'])->name('clients.edit');

Dans app/Http/Controllers/ClientController.php :

public function index() {
    $clients = Client::latest()->paginate(20);
    return view('clients.index', compact('clients'));
}

public function search(Request $request) {
    $clients = Client::where('nom', 'ILIKE', "%{$request->q}%")
        ->orWhere('email', 'ILIKE', "%{$request->q}%")
        ->orWhere('ville', 'ILIKE', "%{$request->q}%")
        ->paginate(20);
    return view('clients.partials.table', compact('clients'));
}

La vue resources/views/clients/index.blade.php contient le champ de recherche live :

@extends('layouts.app')
@section('content')
<div class="max-w-6xl mx-auto p-6">
    <h1 class="text-2xl font-bold mb-4">Mes clients</h1>
    
    <input type="search" name="q" placeholder="Rechercher par nom, email, ville..."
           hx-get="{{ route('clients.search') }}"
           hx-target="#clients-table"
           hx-trigger="keyup changed delay:300ms, search"
           hx-indicator="#search-spinner"
           class="w-full p-3 border rounded mb-4">
    
    <span id="search-spinner" class="htmx-indicator">Recherche en cours...</span>
    
    <div id="clients-table">
        @include('clients.partials.table')
    </div>
</div>
@endsection

L’attribut hx-trigger="keyup changed delay:300ms, search" est crucial : il déclenche la recherche 300 ms après que l’utilisateur arrête de taper, évitant une requête à chaque touche. Le serveur renvoie uniquement le partial clients.partials.table, qui remplace le contenu de #clients-table. La page entière n’est jamais rechargée.

Modale d’édition inline

Pour la modale d’édition, on utilise une approche élégante : un container vide initialement, qu’on remplit dynamiquement avec le formulaire d’édition quand l’utilisateur clique sur « Modifier ».

Dans le partial resources/views/clients/partials/table.blade.php :

<table class="w-full bg-white rounded shadow">
    <thead>
        <tr class="border-b">
            <th class="text-left p-3">Nom</th>
            <th class="text-left p-3">Email</th>
            <th class="text-left p-3">Ville</th>
            <th class="p-3">Actions</th>
        </tr>
    </thead>
    <tbody>
    @foreach($clients as $client)
        <tr id="client-{{ $client->id }}" class="border-b">
            <td class="p-3">{{ $client->nom }}</td>
            <td class="p-3">{{ $client->email }}</td>
            <td class="p-3">{{ $client->ville }}</td>
            <td class="p-3 text-right">
                <button hx-get="{{ route('clients.edit', $client) }}"
                        hx-target="#modal"
                        class="text-blue-600">Modifier</button>
                <button hx-delete="{{ route('clients.destroy', $client) }}"
                        hx-target="#client-{{ $client->id }}"
                        hx-swap="outerHTML swap:300ms"
                        hx-confirm="Supprimer ce client ?"
                        class="text-red-600">Supprimer</button>
            </td>
        </tr>
    @endforeach
    </tbody>
</table>
<div id="modal"></div>

Le contrôleur retourne un partial modal :

public function edit(Client $client) {
    return view('clients.partials.modal', compact('client'));
}

Et la modale dans resources/views/clients/partials/modal.blade.php :

<div class="fixed inset-0 bg-black/50 flex items-center justify-center z-50"
     onclick="if(event.target===this) this.remove()">
    <form hx-put="{{ route('clients.update', $client) }}"
          hx-target="#client-{{ $client->id }}"
          hx-swap="outerHTML"
          class="bg-white rounded p-6 w-full max-w-md">
        @csrf
        @method('PUT')
        <h2 class="text-xl font-bold mb-4">Modifier le client</h2>
        <input name="nom" value="{{ $client->nom }}" required class="w-full mb-2 p-2 border rounded">
        <input name="email" type="email" value="{{ $client->email }}" required class="w-full mb-2 p-2 border rounded">
        <input name="ville" value="{{ $client->ville }}" required class="w-full mb-4 p-2 border rounded">
        <div class="flex justify-end gap-2">
            <button type="button" onclick="document.querySelector('.fixed').remove()" class="px-4 py-2">Annuler</button>
            <button type="submit" class="bg-blue-600 text-white px-4 py-2 rounded">Enregistrer</button>
        </div>
    </form>
</div>

Validation inline côté serveur

L’un des avantages les plus puissants de HTMX est la validation côté serveur en temps réel. Ton contrôleur garde toutes les règles de validation Laravel classiques, et tu retournes simplement un partial avec les erreurs qui remplace l’ancien formulaire :

public function update(Request $request, Client $client) {
    $validated = $request->validate([
        'nom' => 'required|min:2|max:120',
        'email' => 'required|email|unique:clients,email,' . $client->id,
        'ville' => 'required|max:100',
    ]);
    
    $client->update($validated);
    return view('clients.partials.row', compact('client'));
}

En cas d’échec de validation, Laravel renvoie automatiquement un 422 avec les erreurs. HTMX 2 traite les erreurs 422 comme des swaps valides depuis 2024, ce qui simplifie grandement le code. Tu peux aussi ajouter hx-on::response-error="..." pour gérer finement.

Adaptation au contexte ouest-africain

HTMX + Laravel répond particulièrement bien aux contraintes des PME francophones d’Afrique de l’Ouest. Premièrement, la légèreté : 14 Ko de HTMX + 30 Ko de Tailwind via CDN = 44 Ko total côté client, contre 200-500 Ko pour une SPA React minimale. Sur 3G partagée à Bamako ou Conakry, cette différence se traduit par un chargement initial 5 à 10× plus rapide et une consommation data divisée par autant.

Deuxièmement, la simplicité de l’équipe. Une PME à Dakar ou Abidjan trouve facilement des développeurs PHP/Laravel sur le marché local. Trouver des développeurs React/Next.js seniors est plus difficile et plus cher (différence salariale typique de 30-50 %). Avec HTMX, ton équipe Laravel existante peut tout faire.

Troisièmement, la robustesse en réseau dégradé. Une SPA qui plante au milieu d’un parcours métier laisse l’utilisateur dans un état imprévisible. HTMX, par sa nature « petits fragments HTML envoyés à la demande », dégrade gracieusement : si une requête échoue, seul le fragment concerné est dans un état d’erreur, le reste de la page continue de fonctionner.

Erreurs fréquentes à éviter

  • Oublier le CSRF token — sur Laravel, ajoute <meta name="csrf-token" content="{{ csrf_token() }}"> et dans HTMX htmx.config.headers = { 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content }.
  • Charger HTMX deux fois — provoque des doubles requêtes silencieuses. Vérifie ton layout.
  • Trop de granularité — éviter de mettre hx-get sur 50 boutons d’une page. Préfère un parent unique avec event delegation.
  • Pas de feedback visuel — pendant qu’une requête tourne, montre quelque chose. Sinon l’utilisateur clique 3 fois.
  • Sélecteurs CSS fragiles — utilise des id stables (#client-{{$id}}) plutôt que des chaînes complexes .row:nth-child(3).
  • Pas de tests E2E — utilise Pest/Dusk pour valider les flux HTMX critiques.

Trois cas d’usage concrets

  1. Cabinet d’expertise comptable à Abidjan — saisie de pièces comptables avec recherche live de clients/fournisseurs, validation en temps réel, calcul automatique TVA. 12 utilisateurs simultanés sur Hetzner CX22. Coût total infra : 2 700 F CFA/mois.
  2. Plateforme immobilière à Dakar — listing de biens avec filtres dynamiques (prix, surface, ville), pagination infinie, modales de prise de rendez-vous. 200 visites simultanées en pic, le CX22 tient sans transpirer.
  3. SaaS RH à Ouagadougou — gestion des employés et fiches de paie, validation côté serveur, exports PDF asynchrones avec polling HTMX pour le statut. Architecture extrêmement légère.

Checklist post-déploiement

  • ✅ HTMX chargé une seule fois dans le layout
  • ✅ CSRF token configuré pour HTMX
  • ✅ Au moins 1 cas de search live opérationnel
  • ✅ Au moins 1 modale CRUD complète (create + edit + delete)
  • ✅ Validation côté serveur affichée inline
  • ✅ Indicateurs visuels de chargement pour les requêtes > 200 ms
  • ✅ Tests E2E couvrant les flux critiques
  • ✅ Headers de cache appropriés (les fragments ne doivent pas être cachés)
  • ✅ Surveillance du temps de réponse serveur (vise < 100 ms par fragment)

FAQ

HTMX vs Livewire pour Laravel ?

Livewire est plus magique mais plus complexe à déboguer (state synchronisé invisible). HTMX est plus explicite : tu vois exactement quelle URL est appelée et quel HTML revient. Pour les équipes pragmatiques, HTMX est plus prévisible et plus performant.

Peut-on combiner HTMX et Alpine.js ?

Oui, c’est même la combinaison recommandée. HTMX pour les interactions serveur, Alpine.js pour les interactions purement client (toggle, dropdown, validation locale). Voir notre tutoriel Alpine.js.

HTMX scale-t-il en production ?

Oui. Plus de fragments serveur = plus de RPS, mais chaque fragment est minuscule (souvent < 5 Ko). Sur un CX22 bien configuré, 1 000 RPS est atteignable sans problème.

HTMX est-il une mode passagère ?

Non. Adoption massive depuis 2023, intégré dans Django, Rails, Laravel, FastAPI, Phoenix. Soutenu par GitHub, Cloudflare, et des milliers d’entreprises.

Pour aller plus loin

Besoin d’accompagnement HTMX + Laravel ?

Tu construis ou maintiens une application Laravel et tu veux ajouter du dynamisme sans complexifier ? ITSkillsCenter propose un audit gratuit de 30 minutes pour cadrer ton intégration HTMX. Contacte-nous via WhatsApp +221 78 226 83 77 ou demande directement ton audit gratuit en ligne.

Cinq patterns avancés à connaître

Une fois que tu maîtrises les bases CRUD, plusieurs patterns plus avancés te permettent de couvrir des cas d’usage sophistiqués. Le premier pattern est l’infinite scroll. Au lieu d’une pagination classique avec boutons, tu ajoutes hx-get="/clients?page=2" hx-trigger="revealed" hx-swap="afterend" sur le dernier élément de la liste. Quand l’utilisateur scrolle jusqu’à lui, HTMX charge automatiquement la page suivante et l’ajoute. Élégant, sans librairie tierce.

Le deuxième pattern est le polling pour les opérations longues. Imagine un export PDF qui prend 30 secondes côté serveur (job Sidekiq Laravel ou Queue). Tu retournes immédiatement un fragment en cours avec hx-get="/exports/123/status" hx-trigger="every 2s". Toutes les 2 secondes, le client interroge le statut. Quand l’export est prêt, tu retournes le lien de téléchargement final qui remplace le polling. Aucune logique de WebSocket, aucun JavaScript custom.

Le troisième pattern est la cascade de selects. Classique pour les formulaires d’adresse : pays → région → ville. Le select pays a hx-get="/regions" hx-trigger="change" hx-target="#region-select". Quand l’utilisateur sélectionne un pays, le serveur renvoie les régions correspondantes. Idem pour les villes. Avec 0 ligne de JavaScript.

Le quatrième pattern est l’out-of-band swap via hx-swap-oob. Un seul endpoint serveur peut mettre à jour plusieurs zones de la page indépendantes. Par exemple, après création d’un client : tu retournes la nouvelle ligne du tableau ET un toast de confirmation ET le compteur de clients incrémenté. Tout en une seule requête.

Le cinquième pattern est le boost via hx-boost="true". Tu actives ce comportement sur un container parent et tous les liens et formulaires à l’intérieur deviennent automatiquement AJAX, sans aucun autre attribut. C’est le moyen le plus rapide de transformer une application Laravel classique en application AJAX en quelques minutes.

Sécurité : les 4 règles incontournables

HTMX simplifie le code mais ne dispense pas de bonnes pratiques sécurité. Première règle : la validation côté serveur est obligatoire. HTMX ne fournit aucune validation client. Toute donnée entrante doit être validée par les Form Requests Laravel comme dans n’importe quelle application. Deuxième règle : échapper les sorties HTML. Blade le fait par défaut avec {{ }}, mais reste vigilant si tu utilises {!! !!} sur du contenu utilisateur, c’est une porte ouverte aux XSS.

Troisième règle : vérifier l’autorisation sur chaque endpoint. Un attaquant peut très bien envoyer une requête PUT directement à /clients/42 sans passer par ton interface. Utilise les Policies Laravel ou des middlewares d’autorisation systématiquement. Quatrième règle : configurer les headers CSP correctement. Si tu utilises HTMX via CDN, autorise unpkg.com dans script-src. Pour la production, héberge HTMX en local (composer require htmx-php ou simple copie du fichier).

Plan de formation pour ton équipe

Adopter HTMX n’exige pas de formation lourde, mais un transfert structuré accélère l’adoption. Voici le plan que nous utilisons en mission ITSkillsCenter chez nos clients à Dakar et Abidjan : trois demi-journées suffisent. Première demi-journée : philosophie HTMX, les 5 attributs essentiels, et un premier CRUD complet en pair-programming. Deuxième demi-journée : patterns avancés (infinite scroll, polling, OOB), tests E2E avec Pest, débogage. Troisième demi-journée : revue de code des premières fonctionnalités HTMX que l’équipe a écrites elle-même, optimisations spécifiques au projet. Au bout de ces trois demi-journées, ton équipe Laravel est autonome et productive sur HTMX. Le coût total typique en consulting est de 1,2 millions F CFA pour une équipe de 4-6 développeurs, à comparer avec les 3-6 mois nécessaires pour former une équipe à React + Redux + écosystème complet.


[ITS] ITSkillsCenter — formations IT et conseil pour PME d’Afrique de l’Ouest. Dakar · Abidjan · Ouagadougou · Bamako · Conakry. Tous nos contenus sont audités selon notre charte éditoriale Ahl-Sunna.

Besoin d'un site web ?

Confiez-nous la Création de Votre Site Web

Site vitrine, e-commerce ou application web — nous transformons votre vision en réalité digitale. Accompagnement personnalisé de A à Z.

À partir de 250.000 FCFA
Parlons de Votre Projet
Publicité