vursion/laravel-sitemappable
A Laravel package that makes Eloquent models sitemappable, letting you generate XML sitemaps directly from your models with configurable URLs, last-modified dates, priorities, and change frequencies for better search engine indexing.
Install the package and start with the smallest useful flow: generate a sitemap for a known set of public URLs.
composer require spatie/laravel-sitemap
Optionally publish config if you need to tune crawling behavior:
php artisan vendor:publish --provider="Spatie\Sitemap\SitemapServiceProvider"
A minimal crawl-based sitemap:
use Spatie\Sitemap\SitemapGenerator;
SitemapGenerator::create(config('app.url'))
->writeToFile(public_path('sitemap.xml'));
A minimal model/URL-driven sitemap:
use Spatie\Sitemap\Sitemap;
use Spatie\Sitemap\Tags\Url;
Sitemap::create()
->add(Url::create('/'))
->add(Url::create('/about'))
->writeToFile(public_path('sitemap.xml'));
Look first at Spatie\Sitemap\Sitemap, SitemapGenerator, Tags\Url, and the Sitemapable contract. If you add Eloquent models, the 1.12.0 release is relevant: model created_at and updated_at timestamps are now persisted onto sitemap entries, so model-driven sitemaps can carry meaningful <lastmod> data without a separate lookup.
First practical use case: create an artisan command that builds a sitemap for your public marketing pages, blog posts, and product pages, writes it to public/sitemap.xml, and schedules it daily.
Crawl-driven sitemaps
Use SitemapGenerator when you do not already have a clean URL source. Configure crawl limits and profiles before running it, and execute it from a command or queue job rather than a web request.
SitemapGenerator::create(config('app.url'))
->setMaximumCrawlCount(500)
->writeToDisk('public', 'sitemap.xml');
Route/manual sitemaps
Use Sitemap::create()->add(Url::create(...)) for known routes. Set metadata explicitly where it matters:
Url::create(route('posts.show', $post))
->setLastModificationDate($post->updated_at)
->setChangeFrequency(Url::CHANGE_FREQUENCY_WEEKLY)
->setPriority(0.8);
Model-driven sitemaps
Implement Sitemapable on models you want to add directly:
use Spatie\Sitemap\Contracts\Sitemapable;
use Spatie\Sitemap\Tags\Url;
class Post extends Model implements Sitemapable
{
public function toSitemapTag(): Url|string
{
return Url::create(route('posts.show', $this));
}
}
Then add a filtered collection:
Sitemap::create()
->add(Post::query()->published()->get())
->writeToFile(public_path('sitemap.xml'));
With 1.12.0, the model’s created_at and updated_at are available on the resulting sitemap entry. Use this to drive <lastmod> from updated_at instead of duplicating date logic. Check the generated XML in your application to confirm whether your Sitemapable return value or the package sets <lastmod> in your exact setup.
Sitemap indexes and chunking
For large sites, split entries into multiple sitemap files and reference them from a sitemap index. Keep each sitemap below the protocol limits: 50,000 URLs and 50 MB uncompressed.
Scheduling and serving
Generate on a schedule, write to a public disk, and serve the file through a route or web server. A common pattern is a scheduled command writing public/sitemap.xml, plus a lightweight route returning it with application/xml.
Testable generation
Extract sitemap construction into a service or command. In tests, generate to a temporary path, assert the file exists, and parse the XML to assert expected URLs and <lastmod> values.
lastmod should be a valid W3C datetime. Carbon instances generally work, but null timestamps should not produce a <lastmod> element.created_at/updated_at. Models with timestamps disabled or custom date columns need explicit handling.select('id', 'slug', 'updated_at'), to avoid memory pressure.route() and Url::create() depend on APP_URL, request context, and route model binding; verify the generated XML contains absolute production URLs.writeToFile(public_path(...)) and writeToDisk('public', ...) behave differently; confirm disk configuration and permissions.Sitemap::create()->add($allModels) can exhaust memory.SitemapGenerator.Url tags, custom crawl profiles, Sitemapable implementations, and your own command/service layer around generation. Keep sitemap policy in application code rather than scattering it across controllers.How can I help you explore Laravel packages today?