1

I'm using the laravel-seo-sitemap package to generate sitemap XML files in a Laravel 10 application.

I have a paginated blog route like this:

Route::prefix('/blog')->group(function () {
    Route::get('/', [BlogController::class, 'index'])
        ->name('support.blog.index')
        ->priority('0.6')
        ->sitemap();
});

This correctly adds /blog to the sitemap, but I also want to include paginated versions like /blog?page=2, /blog?page=3, etc., since those contain unique and crawlable content (e.g. paginated archive listings).

How can I configure the package to generate sitemap entries for each page of the pagination?

Ideally, I'd like to specify:

  • The total number of pages dynamically (from a model or query)
  • The route pattern to apply pagination (/blog?page=2, /blog?page=3, ...)

Is there a way to extend the sitemap generator with a custom SitemapItemTemplate or hook into the route’s sitemap registration?

https://github.com/VeiligLanceren-nl/laravel-seo-sitemap

I already did try to use the template but it didn't work out as wished.

4

2 Answers 2

0

First of all, thanks for using my package. Based on your question I've made it easier in the package to use it with default Laravel pagination. The trait is available in the 2.1.0 version I just released. Below are two examples for generating paginated sitemap entries in a custom sitemap template:

1. Without using the HasPaginatedSitemap Trait

You can handle pagination logic manually in your template without using the trait:

use Illuminate\Routing\Route;
use VeiligLanceren\LaravelSeoSitemap\Sitemap\Item\Url;

class BlogIndexTemplate implements SitemapItemTemplate
{
    public function generate(Route $route): iterable
    {
        $totalItems = Post::published()->count();
        $perPage = 20;
        $totalPages = (int) ceil($totalItems / $perPage);

        for ($page = 1; $page <= $totalPages; $page++) {
            // If you want to skip page=1 in the query, handle it here
            // if ($page === 1) continue;

            $url = route($route->getName(), ['page' => $page]);
            yield Url::make($url);
        }
    }
}

2. With the HasPaginatedSitemap Trait

Pagination logic is encapsulated in the trait, reducing boilerplate and preventing mistakes:

use Illuminate\Routing\Route;
use VeiligLanceren\LaravelSeoSitemap\Support\Traits\HasPaginatedSitemap;

class BlogIndexTemplate implements SitemapItemTemplate
{
    use HasPaginatedSitemap;

    public function generate(Route $route): iterable
    {
        $totalItems = Post::published()->count();
        $perPage = 20;

        // Optionally, pass true as last argument to skip page=1 in the query string
        yield from $this->paginatedUrls($route, $totalItems, $perPage);
    }
}

Benefits of using the Trait:

  • Less code and less error-prone
  • Consistent pagination handling across templates
  • Supports skipping ?page=1 and passing extra parameters
Sign up to request clarification or add additional context in comments.

1 Comment

This fixed the issue, thank you Niels!
0

You can do it with Spatie Laravel Sitemap package, which allows you to create the sitemap with pagination, example code.

<?php
namespace App\Console\Commands;

use Illuminate\Console\Command;
use Spatie\Sitemap\Sitemap;
use Spatie\Sitemap\Tags\Url;
use App\Models\Blog;

class GenerateSitemap extends Command
{
    protected $signature = 'sitemap:generate';
    protected $description = 'Generate the sitemap including paginated blog pages';

    public function handle()
    {
        $sitemap = Sitemap::create();

        // Add homepage
        $sitemap->add(Url::create('/'));

        // Add main blog page
        $sitemap->add(Url::create('/blog'));

        // Add paginated blog pages
        $perPage = 10;
        $totalPosts = Blog::count();
        $totalPages = (int) ceil($totalPosts / $perPage);

        for ($i = 2; $i <= $totalPages; $i++) {
            $sitemap->add(Url::create("/blog?page={$i}"));
        }

        // Save to public directory
        $sitemap->writeToFile(public_path('sitemap.xml'));

    }
}
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xmlns:video="http://www.google.com/schemas/sitemap-video/1.1" xmlns:news="http://www.google.com/schemas/sitemap-news/0.9">
    <url>
    <loc>http://localhost</loc>
            </url>
    <url>
    <loc>http://localhost/blog</loc>
            </url>
    <url>
    <loc>http://localhost/blog?page=2</loc>
            </url>
    <url>
    <loc>http://localhost/blog?page=3</loc>
            </url>
    <url>
    <loc>http://localhost/blog?page=4</loc>
            </url>
    <url>
    <loc>http://localhost/blog?page=5</loc>
            </url>
    <url>
    <loc>http://localhost/blog?page=6</loc>
            </url>
    <url>
    <loc>http://localhost/blog?page=7</loc>
            </url>
    <url>
    <loc>http://localhost/blog?page=8</loc>
            </url>
    <url>
    <loc>http://localhost/blog?page=9</loc>
            </url>
    <url>
    <loc>http://localhost/blog?page=10</loc>
            </url>
</urlset>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.