Laravel pagination pretty URL

Stefan Bogdanescu

Founder & Senior Architect · 2026-06-29

Laravel Company
# Achieving Pretty URLs: Implementing Route-Based Pagination in Laravel As developers, we often grapple with the balance between functional code and user experience. While simple query string pagination (`?page=3`) works perfectly fine for backend operations, it doesn't offer the clean, semantic structure that modern RESTful applications demand. The desire to move towards cleaner URLs, like `/something/page/3`, is a natural progression in building professional applications. This post will walk you through how to achieve this "pretty URL" pagination structure in Laravel, moving beyond simple query strings and leveraging the power of route definition and controller logic. ## The Problem: Query Strings vs. Route Segments By default, when you use standard Eloquent pagination with methods like `->links()`, Laravel generates URLs based on query parameters: ```html http://example.com/posts?page=3 ``` While functional, this method mixes data filtering (the query string) with the resource identifier (the URL path), which can look cluttered and is less semantic. We want to embed the pagination state directly into the URL structure. ## The Laravel Solution: Route-Based Pagination To achieve the desired structure (`/something/page/3`), we need to stop relying solely on query strings and instead define dynamic routes that capture the pagination parameter as a segment of the URI path. This requires careful setup in your `routes/web.php` file. ### Step 1: Defining the Dynamic Route Instead of relying on standard resource routing, we define a specific route that includes the pagination number as a named segment. In your `routes/web.php`: ```php use App\Http\Controllers\PostController; // Define the custom route structure for paginated items Route::get('/posts/page/{page?}', [PostController::class, 'indexPage']) ->name('posts.page') ->where('page', '[0-9]+'); // Ensure 'page' is only an integer ``` **Explanation:** 1. We are mapping the request directly to a specific controller method (`indexPage`). 2. The `{page?}` segment captures the desired page number from the URL. The `?` makes the segment optional, allowing the base route (e.g., `/posts`) to work without a page number. 3. The `->where('page', '[0-9]+')` is an important safety measure to ensure that only valid numerical inputs are passed to your controller method, enhancing security and robustness—a core principle reflected in the design philosophy behind frameworks like Laravel. ### Step 2: Implementing the Controller Logic Your controller method must now retrieve the `page` number from the route parameters and use it to query the database using Eloquent’s built-in pagination features. In your `PostController.php`: ```php namespace App\Http\Controllers; use App\Models\Post; use Illuminate\Http\Request; class PostController extends Controller { public function indexPage(Request $request, $page) { // Validate the page number (though route definition helps, runtime validation is good practice) if (!is_numeric($page) || $page < 1) { abort(400, 'Invalid page number.'); } // Use Eloquent pagination to fetch the correct set of data $posts = Post::paginate(50); // Example: 50 items per page // Return the view with the paginated results return view('posts.index', compact('posts')); } } ``` ### Step 3: Rendering the Pagination Links The final step is ensuring that when the pagination links are rendered in your Blade view, they follow the new URL structure. Since we are handling the entire request flow within the controller and redirecting to a view, Laravel’s built-in pagination links will naturally generate the correct relative links based on the route definitions you set up. In your `resources/views/posts/index.blade.php`: ```html

Posts

{{-- Display the posts --}} @foreach ($posts as $post)

{{ $post->title }}

@endforeach {{-- Laravel's pagination links will automatically use the route structure we defined --}} {{ $posts->links() }} ``` By using this approach, when a user clicks the "Next" link generated by `{{ $posts->links() }}`, the resulting URL will correctly follow the pattern you established: `/posts/page/4`. ## Conclusion Moving from query string pagination to route-based pagination is a significant step toward building cleaner, more maintainable, and more SEO-friendly applications. It shifts the burden of URL structure management from the view layer into the routing layer, which is where it belongs. As you build complex features within Laravel, always prioritize structuring your routes logically. For deeper insights into architectural patterns that facilitate this kind of clean design, exploring resources from [https://laravelcompany.com](https://laravelcompany.com) will provide excellent guidance on robust development practices.