Looking to hire Laravel developers? Try LaraJobs

laravel-sitemap-generator maintained by ibraheem-ghazi

Description
Easy and flexible Laravel sitemap generator with XSL styling support
Last update
2026/09/05 09:51 (dev-main)
License
Links
Downloads
1
Tags

Comments
comments powered by Disqus

Laravel Sitemap Generator

A flexible Laravel package for generating XML sitemaps from Eloquent queries and custom URLs.

It provides:

  • Separate sitemap classes for posts, products, pages, and other resources.
  • Registration through configuration or PHP code.
  • A make:sitemap Artisan command for generating class stubs.
  • A sitemap index at public/sitemap.xml.
  • Individual sitemap files under public/sitemaps.
  • Optional monthly splitting based on created_at.
  • Optional XSL styling for browser-friendly XML output.
  • A locale-aware helper trait for alternate URLs.

Why This Package?

Applications often need multiple sitemap files for different resources. This package keeps each sitemap's query, URL mapping, output name, and chunking rules in a dedicated class. The package handles the Laravel integration, registration, file generation, and sitemap index.

The package is an orchestration layer around spatie/laravel-sitemap. Spatie provides the sitemap objects and XML renderer; this package adds Laravel-specific sitemap classes, Eloquent support, date splitting, registration, and optional XSL publishing.

Requirements and Dependencies

  • PHP 8.0 or newer.
  • Laravel 8, till 13.
  • spatie/laravel-sitemap 7.3 or newer.
  • Query-backed models must have a created_at attribute unless the age-based methods are overridden.

The package uses PHP 8 features such as typed properties, union types, and the nullsafe operator.

Installation

composer require ibraheem-ghazi/laravel-sitemap-generator

Laravel discovers SitemapGeneratorProvider automatically through Composer package discovery.

Publishing

Publish the configuration:

php artisan vendor:publish --tag=sitemap-config

Publish the optional XSL stylesheet:

php artisan vendor:publish --tag=sitemap-assets

Publish both files:

php artisan vendor:publish --provider="IbraheemGhazi\SitemapGenerator\SitemapGeneratorProvider"

The configuration is published to config/sitemap.php. The stylesheet is published to public/sitemap-styles.xsl. When the stylesheet exists, generated XML includes a reference to /sitemap-styles.xsl. It changes browser presentation only and does not change the sitemap data.

Configuration

The published config/sitemap.php contains two settings:

<?php

return [
    'files' => [
        // App\Sitemaps\PostSitemap::class,
    ],

    'supported_locales' => [
        app()->getLocale(),
    ],
];

files

Add sitemap classes here to include them in every call to generate():

'files' => [
    App\Sitemaps\PostSitemap::class,
    App\Sitemaps\ProductSitemap::class,
],

Configured classes are combined with runtime registrations, and duplicates are removed.

supported_locales

This setting is used by the HasAlternateUrls trait. By default, it contains the current application locale:

'supported_locales' => [
    app()->getLocale(),
],

For a localized application, configure the supported locales explicitly:

'supported_locales' => [
    'en',
    'fr',
    'de',
],

Creating a Sitemap Class

Each sitemap class extends AbstractSitemapFile and defines a filename, query, and model-to-URL conversion:

<?php

namespace App\Sitemaps;

use App\Models\Post;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use IbraheemGhazi\SitemapGenerator\AbstractSitemapFile;
use Spatie\Sitemap\Tags\Url;

class PostSitemap extends AbstractSitemapFile
{
    protected string $filename = 'posts';

    public function getQuery(): ?Builder
    {
        return Post::query()->where('published', true);
    }

    public function modelToSitemapUrl_Impl(Model $model): ?Url
    {
        /** @var Post $model */
        return Url::create(route('posts.show', $model))
            ->setLastModificationDate($model->updated_at);
    }
}

modelToSitemapUrl_Impl() must return a Spatie Url object for every model. The generated stub returns null intentionally and must be completed.

The base class automatically assigns priority and change frequency using the model's age. It calculates age from created_at and the current time.

Default priority rules:

Age Priority
Less than 7 days 0.9
Less than 30 days 0.8
Less than 180 days 0.7
180 days or older 0.5

Default change-frequency rules:

Age Frequency
Less than 30 days daily
30 days or older weekly

Override getPriorityByAge() or getFrequencyByAge() when your application needs different rules.

Custom URLs

Use getItems() for URLs that do not come from a model query:

use IbraheemGhazi\SitemapGenerator\AbstractSitemapFile;
use Spatie\Sitemap\Tags\Url;

class StaticPagesSitemap extends AbstractSitemapFile
{
    protected string $filename = 'static-pages';

    public function getItems(): array
    {
        return [
            Url::create(route('home')),
            Url::create(route('contact')),
            Url::create(route('about')),
        ];
    }
}

A sitemap class can provide only custom items, only a query, or both. You can use Spatie metadata methods such as setLastModificationDate(), setPriority(), and setChangeFrequency() when creating a Url.

For model URLs, the base class assigns priority and frequency after your implementation method returns. Those automatic values take precedence unless you override the relevant base behavior.

Generating Sitemaps

Generate every configured and runtime-registered sitemap:

use IbraheemGhazi\SitemapGenerator\SitemapGenerator;

SitemapGenerator::make()->generate();

generate() deletes and recreates public/sitemaps, generates each non-empty child sitemap, and writes a new sitemap index. Treat that directory as generated build output.

Runtime registration

use App\Sitemaps\PostSitemap;
use IbraheemGhazi\SitemapGenerator\SitemapGenerator;

SitemapGenerator::register(PostSitemap::class);
SitemapGenerator::make()->generate();

Remove a runtime registration with:

SitemapGenerator::unregister(PostSitemap::class);

Runtime registrations are held statically for the current PHP process. Use config/sitemap.php for persistent registration.

Refresh one sitemap

use App\Sitemaps\PostSitemap;

PostSitemap::refresh();

Or:

SitemapGenerator::make()->refreshBySitemapFile(
    new PostSitemap()
);

This regenerates the selected child file and rebuilds public/sitemap.xml without first deleting every child file.

Console Generator

Create a sitemap class stub under app/Sitemaps:

php artisan make:sitemap Post

This creates app/Sitemaps/PostSitemap.php with a filename, a disabled chunking flag, and stubs for getQuery() and modelToSitemapUrl_Impl().

Generate a date-chunked class:

php artisan make:sitemap Post --chunked
# or
php artisan make:sitemap Post -c

Nested names are supported:

php artisan make:sitemap Admin/Post --chunked

This creates app/Sitemaps/Admin/PostSitemap.php in the App\Sitemaps\Admin namespace. The command refuses to overwrite an existing class. Complete the generated methods, then add the class to config/sitemap.php or register it at runtime.

Date Chunking

Set $chunkedByDate to true to split query results into monthly files:

class PostSitemap extends AbstractSitemapFile
{
    protected string $filename = 'posts';
    protected bool $chunkedByDate = true;

    public function getQuery(): ?Builder
    {
        return Post::query()->where('published', true);
    }

    public function modelToSitemapUrl_Impl(Model $model): ?Url
    {
        return Url::create(route('posts.show', $model));
    }
}

Example output:

public/sitemaps/posts-2026-09.xml
public/sitemaps/posts-2026-08.xml

Date chunking uses the query's created_at year and month. URLs returned by getItems() are written to a separate file:

public/sitemaps/posts-others.xml

Date chunking requires a non-null Eloquent query. Empty months and empty sitemap files are not written.

Generated Sitemap Files

Sitemap index

The generator writes:

public/sitemap.xml

This is a <sitemapindex> XML document. It contains one <sitemap> entry for each generated child file under public/sitemaps, using URLs based on the application's configured URL.

Regular sitemap

For $filename = 'posts' without chunking:

public/sitemaps/posts.xml

This is a <urlset> XML document. Each <url> contains the URL and any metadata provided by the Spatie Url object, such as last modification date, change frequency, and priority.

Browser display

If the XSL asset is published, opening the XML in a browser displays the styled sitemap index or URL table. Search engines still consume the underlying XML. The generated stylesheet reference is /sitemap-styles.xsl, so the application must serve that path publicly.

Search engine submission

Submit the index URL rather than every child file:

https://example.com/sitemap.xml

You can also reference it from robots.txt:

Sitemap: https://example.com/sitemap.xml

Ensure APP_URL, route URL generation, and web-server public file serving use the correct production domain.

Alternate URL Trait

HasAlternateUrls helps a class produce URLs for multiple locales. The consuming class must implement:

public function url(?string $locale = null): string

Example:

use IbraheemGhazi\SitemapGenerator\Traits\HasAlternateUrls;

class LocalizedPage
{
    use HasAlternateUrls;

    public function url(?string $locale = null): string
    {
        $locale = $locale ?: app()->getLocale();

        return url("/{$locale}/about");
    }
}

With this configuration:

'supported_locales' => ['en', 'fr', 'de'],

getAlternateUrls() returns a map like:

[
    'x-default' => 'https://example.com/en/about',
    'en' => 'https://example.com/en/about',
    'fr' => 'https://example.com/fr/about',
    'de' => 'https://example.com/de/about',
]

The trait only builds this locale-to-URL map. It does not automatically attach alternate links to a Spatie Url object. Use the map with the alternate-link support available in your sitemap workflow.

The x-default URL is based on the current application locale. The locale list is read from config('sitemap.supported_locales').

API Summary

SitemapGenerator

  • make() returns the application singleton.
  • register(string $class) registers a sitemap class for the current process.
  • unregister(string $class) removes a runtime registration.
  • getRegisteredSitemaps() returns configured and runtime classes.
  • generate() rebuilds all child files and the index.
  • refreshBySitemapFile(AbstractSitemapFile $file) refreshes one file and the index.
  • homePageUrl() returns a daily, priority-1 Spatie home-page URL.

AbstractSitemapFile

  • getFileName() returns the output filename.
  • getQuery() returns an optional Eloquent builder.
  • getItems() returns custom Spatie Url objects.
  • modelToSitemapUrl_Impl() converts a model into a Spatie Url.
  • isChunkedByDate() reports whether monthly splitting is enabled.
  • refresh() refreshes the current sitemap class.

Operational Notes

  • Run generation from a command, scheduler, deployment step, or queue appropriate for the data size.
  • Query URL generation uses Eloquent cursors, but date chunking first queries distinct year/month combinations.
  • Do not store unrelated public files inside public/sitemaps; generate() deletes that directory.
  • A class with no query results and no custom items does not create an empty child file, but the index is still regenerated.
  • Generated sitemap URLs must be publicly reachable.