L’infinite scroll (défilement infini) transforme l’expérience utilisateur en chargeant du contenu de façon fluide, sans pagination intrusive. Grâce à Inertia.js v2+, cette fonctionnalité est désormais native, performante et facile à implémenter dans une stack Laravel + Vue 3.
Contrairement aux solutions manuelles (gestion d’API, état, scroll, erreurs), Inertia.js fournit une intégration full-stack cohérente : le backend prépare les données, le frontend les consomme intelligemment — avec fusion automatique, synchronisation d’URL, et gestion fine du chargement.
#Sommaire
- Pourquoi utiliser l’infinite scroll ?
- Prérequis techniques
- Configuration côté serveur (Laravel)
- Mise en place côté client (Vue 3)
- Fonctionnalités avancées
- Bonnes pratiques
- Conclusion
#Pourquoi utiliser l’infinite scroll ?
L’infinite scroll améliore :
- L’engagement utilisateur : pas de clics répétitifs sur « page suivante ».
- La fluidité : le contenu arrive naturellement au scroll.
- La modernité : attendu sur les réseaux sociaux, blogs, marketplaces, chats.
⚠️ À utiliser avec discernement : il n’est pas adapté à tous les cas (ex. résultats de recherche paginés où la navigation précise est utile).
#Prérequis techniques
Assurez-vous d’utiliser les versions minimales suivantes :
- Frontend :
@inertiajs/vue3 ≥ v2.2.0 - Backend :
inertiajs/inertia-laravel ≥ v2.0.10
Mettez à jour si nécessaire :
npm install @inertiajs/vue3@latestcomposer require inertiajs/inertia-laravel:^2.0npm install @inertiajs/vue3@latestcomposer require inertiajs/inertia-laravel:^2.0
#Configuration côté serveur (Laravel)
Utilisez Inertia::scroll() pour préparer vos données paginées. Cette méthode :
- Normalise les métadonnées de pagination (
next,prev,current_page, etc.) - Active la fusion automatique côté frontend (ajout, pas remplacement)
use Inertia\Inertia;Route::get('/blog', function () {$posts = Post::published()->with('category')->latest()->paginate(6);return Inertia::render('Blog/Index', ['posts' => Inertia::scroll($posts),]);});use Inertia\Inertia;Route::get('/blog', function () {$posts = Post::published()->with('category')->latest()->paginate(6);return Inertia::render('Blog/Index', ['posts' => Inertia::scroll($posts),]);});
✅ Compatible avec :
paginate()simplePaginate()cursorPaginate()- Eloquent API Resources
💡 Astuce : Si vous filtrez les données (ex. par catégorie), réinitialisez le scroll avec
reset: ['posts']dans vos requêtes Inertia :router.visit('/blog', { data: { category: 'dev' }, reset: ['posts'] });router.visit('/blog', { data: { category: 'dev' }, reset: ['posts'] });
#Mise en place côté client (Vue 3)
Le composant <InfiniteScroll> gère tout : détection du scroll, chargement, fusion.
#🔹 Structure de base
<template><InfiniteScroll data="posts"><article v-for="post in posts.data" :key="post.id">{{ post.title }}</article></InfiniteScroll></template><script setup>import { InfiniteScroll } from '@inertiajs/vue3'</script><template><InfiniteScroll data="posts"><article v-for="post in posts.data" :key="post.id">{{ post.title }}</article></InfiniteScroll></template><script setup>import { InfiniteScroll } from '@inertiajs/vue3'</script>
Le prop
data="posts"correspond à la clé utilisée dansInertia::render([... 'posts' => ...]).
#Fonctionnalités avancées
#1. Chargement anticipé (buffer)
Charge le contenu avant d’atteindre la fin :
<InfiniteScroll data="posts" :buffer="800" /><InfiniteScroll data="posts" :buffer="800" />
Plus la valeur est élevée, plus le chargement est précoce — mais risque de charger du contenu inutile.
#2. Synchronisation d’URL
Par défaut, l’URL est mise à jour avec ?page=2, ?page=3, etc., selon la page la plus visible.
Désactivez-le pour du contenu secondaire :
<InfiniteScroll data="comments" preserve-url /><InfiniteScroll data="comments" preserve-url />
#3. Mode manuel après N pages
Idéal pour limiter les appels automatiques :
<InfiniteScroll data="posts" :manual-after="3"><template #next="{ loading, fetch, hasMore, manualMode }"><button v-if="manualMode && hasMore" @click="fetch" :disabled="loading"><Loader2 v-if="loading" class="animate-spin" />{{ loading ? 'Chargement...' : 'Charger plus' }}</button></template></InfiniteScroll><InfiniteScroll data="posts" :manual-after="3"><template #next="{ loading, fetch, hasMore, manualMode }"><button v-if="manualMode && hasMore" @click="fetch" :disabled="loading"><Loader2 v-if="loading" class="animate-spin" />{{ loading ? 'Chargement...' : 'Charger plus' }}</button></template></InfiniteScroll>
Les slots
#previouset#nextreçoivent :
loading,loadingNext,loadingPreviousfetch()→ déclenche le chargementhasMore,hasNext,hasPreviousmanualMode/autoMode
#4. Slot de chargement par défaut
<template #loading><div class="text-center py-4 text-muted">Chargement des articles…</div></template><template #loading><div class="text-center py-4 text-muted">Chargement des articles…</div></template>
Affiché pendant le chargement automatique si aucun slot before/after n’est fourni.
#5. Chargement dans les deux sens
Par défaut, le composant charge vers le haut et vers le bas.
Restreignez la direction :
<!-- Seulement vers le bas --><InfiniteScroll data="posts" only-next /><!-- Seulement vers le haut --><InfiniteScroll data="messages" only-previous /><!-- Seulement vers le bas --><InfiniteScroll data="posts" only-next /><!-- Seulement vers le haut --><InfiniteScroll data="messages" only-previous />
#6. Mode inversé (chat, timeline)
Pour les interfaces où les nouveaux éléments sont en bas :
<InfiniteScroll data="messages" reverse :auto-scroll="false"><!-- Messages du plus ancien au plus récent --></InfiniteScroll><InfiniteScroll data="messages" reverse :auto-scroll="false"><!-- Messages du plus ancien au plus récent --></InfiniteScroll>
⚠️ Triez vos données correctement côté serveur. Le composant gère le scroll, pas l’ordre.
#7. Conteneurs de scroll personnalisés
Fonctionne dans n’importe quel conteneur scrollable :
<div style="height: 500px; overflow-y: auto;"><InfiniteScroll data="posts">...</InfiniteScroll></div><div style="height: 500px; overflow-y: auto;"><InfiniteScroll data="posts">...</InfiniteScroll></div>
#8. Plusieurs scrolls sur une même page
Évitez les conflits d’URL avec pageName :
// Backend'users' => Inertia::scroll(User::paginate(pageName: 'users')),'orders' => Inertia::scroll(Order::paginate(pageName: 'orders')),// Backend'users' => Inertia::scroll(User::paginate(pageName: 'users')),'orders' => Inertia::scroll(Order::paginate(pageName: 'orders')),
→ URL : ?users=2&orders=3
#9. Accès programmatique
<script setup>import { ref } from 'vue'const scrollRef = ref()const loadMore = () => scrollRef.value?.fetchNext()</script><template><button @click="loadMore">Charger plus</button><InfiniteScroll ref="scrollRef" data="posts" manual /></template><script setup>import { ref } from 'vue'const scrollRef = ref()const loadMore = () => scrollRef.value?.fetchNext()</script><template><button @click="loadMore">Charger plus</button><InfiniteScroll ref="scrollRef" data="posts" manual /></template>
Méthodes exposées : fetchNext(), fetchPrevious(), hasNext(), hasPrevious().
#10. Ciblage d’éléments (tables, listes complexes)
Pour les <table>, <ul>, etc. :
<InfiniteScroll data="users" items-element="#table-body"><table><tbody id="table-body"><tr v-for="user in users.data" :key="user.id">...</tr></tbody></table></InfiniteScroll><InfiniteScroll data="users" items-element="#table-body"><table><tbody id="table-body"><tr v-for="user in users.data" :key="user.id">...</tr></tbody></table></InfiniteScroll>
Ou avec des ref :
<InfiniteScroll:items-element="() => tableBody":start-element="() => tableHeader":end-element="() => tableFooter"><InfiniteScroll:items-element="() => tableBody":start-element="() => tableHeader":end-element="() => tableFooter">
#Bonnes pratiques
| Recommandation | Raison |
|---|---|
| 6–10 éléments/page | Équilibre UX/performance |
Optimisez les requêtes (with(), select()) |
Réduisez la taille des payloads |
| Prévoyez un fallback (bouton "Charger plus") | Accessibilité (JS désactivé, préférences utilisateur) |
| Évitez les composants lourds dans les listes | Performance du scroll |
Réinitialisez avec reset lors de changement de filtre |
Évite le mélange de résultats |
| Testez sur mobile | Le scroll peut être plus sensible |
#Conclusion
Avec Inertia::scroll() et <InfiniteScroll>, vous bénéficiez d’une solution full-stack cohérente, performante et riche en fonctionnalités, sans avoir à gérer manuellement les appels API, la fusion d’état ou la gestion du scroll.
C’est l’outil idéal pour :
- Blogs et flux d’articles
- Réseaux sociaux
- Marketplaces
- Interfaces de chat
- Timelines d’activité
Grâce à Inertia.js, l’infinite scroll devient simple, fiable et maintenable — même dans des applications complexes.
#Ressources officielles
✨ Prêt à moderniser votre interface ? Implémentez l’infinite scroll dès aujourd’hui et offrez une expérience utilisateur fluide et engageante.