Why Use Infinite Scroll (Load More on Page Scroll) in Laravel 11 Applications?
Infinite scrolling, also known as "load more data on scroll," has become a staple in modern web development because it transforms how users interact with content-heavy pages. Unlike traditional pagination — where users must click "Next" buttons repeatedly — infinite scroll automatically fetches and appends new records as the visitor scrolls toward the bottom of the page. This creates a fluid, uninterrupted browsing experience that feels more natural, especially on mobile devices where swiping is the primary gesture.One of the biggest advantages is dramatically increased user engagement. Studies and real-world examples from platforms like Instagram, Twitter (now X), and TikTok show that removing friction (like page reloads or button clicks) encourages users to consume more content. In Laravel applications — such as blogs, product catalogs, news feeds, social dashboards, or e-commerce listings — this translates to higher time-on-site metrics, lower bounce rates, and more page views per session. For instance, sites that switched to infinite scroll have reported up to 15% reductions in bounce rates and significantly longer average sessions, as users "fall into" the content stream without artificial stopping points.
From a technical perspective, infinite scroll pairs perfectly with Laravel's powerful pagination system. Using ->paginate() on the backend combined with jQuery (or modern alternatives like Intersection Observer) for frontend AJAX calls, you can deliver seamless experiences without overloading the initial page load. This approach is particularly effective for large datasets: only load 9–15 items initially, then fetch more on demand — improving performance, reducing server strain on first visits, and providing lazy loading benefits.
Additionally, infinite scroll excels in discovery-driven interfaces where content is homogeneous and users browse casually (e.g., posts, images, products). It minimizes interaction costs, supports serendipitous content discovery, and aligns with mobile-first design trends. While not ideal for goal-oriented tasks (like search results where users need to jump to specific pages), in the right Laravel context — such as a feed-style blog or gallery — it boosts retention, makes your app feel more modern and addictive, and ultimately drives better user satisfaction and business outcomes.
In short, implementing infinite scroll in Laravel 11 isn't just about aesthetics — it's a strategic UX enhancement that keeps visitors scrolling, exploring, and staying longer on your site.

Table Of Content
1 Prerequisites
- PHP 8.2+
- Composer
- MySQL or compatible database
- Basic knowledge of Laravel 11, Blade, and jQuery
2 Introduction
In this Laravel 11 tutorial, you'll learn how to implement load more data on scroll using jQuery and AJAX. We'll use Laravel's built-in pagination combined with an AJAX call to fetch additional records dynamically from the database.
3 Create / Install a Laravel Project
3.1 Install Laravel Project
composer create-project laravel/laravel laravel-infinite-scroll
Navigate to your project directory:
cd laravel-infinite-scroll
3.2 Configure MySql Database
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel_infinite
DB_USERNAME=root
DB_PASSWORD=
4 Create Migration
php artisan make:migration create_posts_table
In the migration file located at database/migrations, add the following code to define the posts table structure:
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->text('content');
$table->string('image')->nullable();
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('posts');
}
};
Run the migration:
php artisan migrate
5 Create Model
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
use HasFactory;
protected $fillable = ['title', 'content', 'image'];
}
6 Create Factory Class
php artisan make:factory PostFactory --model=Post
Update the database/factories/PostFactory.php with:
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Post>
*/
class PostFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array
*/
public function definition(): array
{
return [
'title' => fake()->sentence(6),
'content' => fake()->paragraph(3),
'image' => fake()->imageUrl(800, 600, 'tech'),
];
}
}
To generate 100 dummy records, use:
php artisan tinker
App\Models\Post::factory()->count(120)->create()
exit
7 Create PostController with Pagination Logic
php artisan make:controller PostController
In PostController.php, create an index function to implement the lazy loading pagination logic:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Post;
class PostController extends Controller
{
public function index(Request $request)
{
$posts = Post::latest()->paginate(9); // 9 items per load
if ($request->ajax()) {
$view = view('posts.data', compact('posts'))->render();
return response()->json(['html' => $view]);
}
return view('posts.index', compact('posts'));
}
}
?>
8 Create view (Blade)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Laravel 11 Infinite Scroll - Load More on Scroll</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
</head>
<body class="bg-light">
<div class="container my-5">
<h1 class="text-center mb-5">Laravel 11: Infinite Scroll with jQuery AJAX</h1>
<div id="posts-container" class="row">
@include('posts.data')
</div>
<div id="loading" class="text-center my-4" style="display: none;">
<div class="spinner-border text-primary" role="status"></div>
<p>Loading more posts...</p>
</div>
<div id="no-more" class="text-center my-4 text-muted" style="display: none;">
No more posts to load.
</div>
</div>
<script>
let page = 2; // Start from page 2 since page 1 is already loaded
let loading = false;
$(window).scroll(function() {
if ($(window).scrollTop() + $(window).height() > $(document).height() - 200) {
if (!loading) {
loadMorePosts();
}
}
});
function loadMorePosts() {
loading = true;
$('#loading').show();
$.ajax({
url: '{{ route("posts.index") }}?page=' + page,
type: 'GET',
dataType: 'json',
success: function(response) {
if (response.html.trim() === '') {
$('#loading').hide();
$('#no-more').show();
return;
}
$('#posts-container').append(response.html);
page++;
$('#loading').hide();
loading = false;
},
error: function() {
$('#loading').hide();
console.log('Error loading more posts');
loading = false;
}
});
}
</script>
</body>
</html>
Create Partial viewdata.blade.php for the dynamic data:
@forelse ($posts as $post)
<div class="col-md-4 mb-4">
<div class="card h-100 shadow-sm">
@if($post->image)
<img src="{{ $post->image }}" class="card-img-top" alt="{{ $post->title }} thumbnail" loading="lazy">
@endif
<div class="card-body">
<h5 class="card-title">{{ $post->title }}</h5>
<p class="card-text text-muted">{{ Str::limit($post->content, 120) }}</p>
</div>
</div>
</div>
@empty
<p class="text-center">No posts found.</p>
@endforelse
9 Define Routes
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\PostController;
Route::get('/', function () {
return view('welcome');
});
Route::get('/posts', [PostController::class, 'index'])->name('posts.index');
10 Folder Structure
11 Run Laravel Server to Test the App
php artisan serve
Visit: http://127.0.0.1:8000/posts
Scroll down to see more posts load automatically!
Modern Improvements & Best Practices
- Replace jQuery scroll listener with Intersection Observer API for better performance.
- Use Laravel's Vite to bundle assets instead of CDNs.
- Add throttling/debouncing to scroll event to prevent excessive calls.
- Implement proper loading spinners and error handling.
- Consider Livewire or Inertia.js for more reactive UIs without jQuery.
- Optimize images with loading="lazy" and proper alt texts.
12 Conclusion
Implementing infinite scroll (load more data on page scroll) in Laravel 11 with jQuery AJAX is a straightforward yet powerful way to elevate your web application's user experience. By combining Laravel's efficient pagination, simple AJAX requests, and dynamic content appending, you've created a seamless, mobile-friendly feed that loads content effortlessly as users explore.
This technique reduces friction, boosts engagement, improves performance through lazy loading, and makes your site feel contemporary — just like leading social platforms. While we've used classic jQuery for accessibility and simplicity, consider evolving to Intersection Observer, Laravel Livewire, or Inertia.js for even smoother, more performant results in production apps.
Now it's your turn: apply this to your Laravel project, test with real users, and watch session times grow. Happy coding — and keep scrolling!
If you found this Laravel 11 infinite scroll tutorial helpful, share it with fellow developers or drop a comment with your improvements!
Written by Revathi M
PHP Developer & Technical Writer · 10+ years building web applications with CodeIgniter and Laravel
Revathi specializes in PHP backend development, authentication systems, and REST API design. She writes practical, production-tested tutorials at Get Sample Code to help developers build secure applications faster.
Frequently Asked Questions
You need PHP 8.2+, Composer, MySQL, and a fresh Laravel 11 installation. Basic knowledge of Laravel migrations, models, controllers, and jQuery is helpful.
Create a migration for the 'posts' table with fields: id, name (string), description (text), image (string), timestamps. Run php artisan migrate, then seed 100 dummy records using PostFactory and Tinker.
In PostController::index(), use Post::paginate(6). If $request->ajax(), render the 'data' view as JSON and return it. Otherwise, return the main 'posts' view.
Use jQuery to listen for $(window).scroll(). When near the bottom, increment a page counter and make an AJAX GET to 'posts?page=' + page. Append the response HTML to #data-wrapper.
In posts.blade.php, include a #data-wrapper div with initial posts. In data.blade.php, loop through $posts and display each as a Bootstrap card with image, name, and description.
Common issues: jQuery not loaded (check CDN), incorrect scroll detection logic, or AJAX URL mismatch. Verify console errors and ensure pagination limit isn't reached.
Add a .auto-load-message div. In AJAX beforeSend, show it with 'Loading...'. Hide it in success callback after appending data.
In AJAX success, if response.html is empty, display 'We don't have more data' in the loading message and stop further requests.
Yes, the tutorial uses Bootstrap 5 for card styling. It works seamlessly—ensure you include Bootstrap CSS/JS CDNs in posts.blade.php.
Add error handling in AJAX (e.g., alert on failure), use Laravel caching for posts, or switch to modern JS like Intersection Observer for better performance without jQuery.
