laravel-sitemap: sitemaps built by crawling, or by listing
Create and generate sitemaps with ease
At a glance
- What is it?
- A Spatie package that generates sitemaps from a live URL crawl, from model records, or from both, and can split one crawl into several files with an index.
- Who is it for?
- The choice this package makes for you is whether a sitemap is a generated artifact or a declared one, and it quietly supports both. The crawl path is the fastest route to a complete sitemap on a site that already exposes every page, while the `Sitemapable` interface is the right answer when your important URLs come from a database and a crawler would need to reach them through pagination or filters.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 61 days ago.
- What is it written in?
- Mainly PHP, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 7, 2026, and from our analysis. They are not legal advice.
Editorial analysis
One call that crawls the whole site
The shortest useful example is a single statement with a base URL and a destination path:
use Spatie\Sitemap\SitemapGenerator;
SitemapGenerator::create('https://example.com')->writeToFile($path);That is the package's main argument, stated in the README's second line: it generates a sitemap without you having to add URLs manually, by crawling the entire site. For a site where every page is reachable by link, that is genuinely less work than maintaining a list.
The trade-off follows directly from the approach. A crawler only sees what it can reach, so anything behind authentication, behind a form, or excluded by a noindex rule is absent unless you add it separately. That is not a flaw so much as the boundary of the design, and it is why the package supports the other two modes as first-class citizens rather than as workarounds.
The manual path and the hybrid path
Building a sitemap by hand uses a fluent chain over `Sitemap` and `Url` tags, with Carbon available for timestamps:
use Carbon\Carbon;
use Spatie\Sitemap\Sitemap;
use Spatie\Sitemap\Tags\Url;
Sitemap::create()
->add(Url::create('/home')
->setLastModificationDate(Carbon::yesterday()))
->add(...)
->writeToFile($path);The hybrid path is the one worth remembering, because it is what makes the package usable on a real site. You take the crawl and add to it:
use Spatie\Sitemap\SitemapGenerator;
use Spatie\Sitemap\Tags\Url;
SitemapGenerator::create('https://example.com')
->getSitemap()
->add(Url::create('/extra-page')
->setLastModificationDate(Carbon::yesterday()))
->add(...)
->writeToFile($path);`getSitemap()` returns the collected sitemap object rather than writing it out, so you can append, adjust, then write once. On a site where the crawler covers most pages and a handful of important ones need explicit treatment, this is the shape you will actually use.
For Laravel applications whose URLs come from Eloquent models, there is a third route: implement the `Sitemapable` contract on the model and return a route, a `Url`, or an array of them:
class Post extends Model implements Sitemapable
{
public function toSitemapTag(): Url | string | array
{
return route('blog.post.show', $this);
}
}Note the declared return type allows `Url | string | array`, so a model can yield one tag, a bare route string, or several entries without extra ceremony.
Splitting one crawl into grouped files
This is the newest documented feature and the most useful one for anyone who cares about search coverage. Instead of writing a single file, you pass a closure to `writeToFile`. It receives each `Url` and returns the path that URL belongs in:
SitemapGenerator::create('https://example.com')
->writeToFile(fn (Url $url) => str_starts_with($url->path(), '/blog')
? public_path('sitemap-blog.xml')
: public_path('sitemap-pages.xml'));The README states the motivation directly: separate files, for example one for the blog and one for the rest, make it easier to track indexing coverage per section in Google Search Console.
Returning `null` from the closure leaves that URL out of every sitemap, which gives you a filter and a router in one callback.
By default no sitemap index is written. `sitemapIndexPath` adds one that references each written file:
SitemapGenerator::create('https://example.com')
->sitemapIndexPath(public_path('sitemap.xml'))
->writeToFile(fn (Url $url) => str_starts_with($url->path(), '/blog')
? public_path('sitemap-blog.xml')
: public_path('sitemap-pages.xml'));Combining that with `maxTagsPerSitemap()` splits an over-limit group further into numbered files such as `sitemap-blog_0.xml` and `sitemap-blog_1.xml`, each listed in the index. The interaction between the two options is the part that is easy to miss, since the numbered files only appear when both are in play.
Version 8 and what it required
The 8.0.1 release is the one to know about if you are upgrading, and its title says it plainly: v8 upgraded to crawler v9, Pest v4, and PHP 8.4 or newer. Since the sitemap generator is built on a crawler, that dependency bump is visible in behaviour as well as in the composer requirements.
Two later additions round out the current line. 8.1.0 in March 2026 added a `sort()` method to the sitemap for consistent URL ordering, which matters because unordered output makes diffs noisy and makes it harder to see what actually changed between two crawls. 8.2.0 in June 2026 added the grouping feature described above, and the same release carries two Dependabot bumps for `fetch-metadata`.
That release list tells you something about the project's rhythm. The interesting changes are small and deliberate, arriving alongside routine dependency bumps rather than in a burst of refactoring.
The repository itself is conventional for Spatie packages: `src/` for the code, `config/` for configuration, `docs/` for documentation sources, `resources/`, and a `tests/` directory. Static analysis is wired up through `phpstan.neon.dist` with a `phpstan-baseline.neon`, and the CI badges in the README cover test status, code style, PHPStan and total downloads. An `UPGRADING.md` sits alongside `CHANGELOG.md`, so major version transitions are documented separately from the running log.
Where the README stops and the docs begin
The README is a good tour and a short one. It shows the crawl, the manual build, the hybrid, the split, and the model interface, and each sample is complete enough to paste. What it does not contain is the configuration reference, the tag options beyond `setLastModificationDate`, the `maxTagsPerSitemap` details, or guidance on scheduling the crawl.
All documentation is on spatie.be, at the docs site rather than in the repository. The README says so in one line under a Documentation heading and links it. The repository does ship a `docs/` directory, so the source for that site is there even though the built pages are not.
There is also a funding section, and it is unusually prominent for a README. Spatie asks for sponsorship, describes it as investment in open source packages, suggests buying one of their paid products, and requests postcards from people using the packages, which are published on a virtual postcard wall. That is not a criticism so much as something to expect: the package is free and MIT licensed, and the README is explicit about how the author prefers that be supported.
For the license itself there is nothing to reconcile. The metadata reports MIT, `LICENSE.md` is in the tree, and the README's license section says the same thing. The test suite runs with `composer test`.
Where this fits against the alternatives
The obvious comparison is generating sitemaps without a dependency. A hand-written XML writer is a hundred lines and no composer requirement, and for a static site it is entirely adequate.
The case for this package is specifically Laravel. The `Sitemapable` contract, the `Url` tag objects, the Carbon integration and the automatic crawl are all things you would otherwise assemble yourself, and the closure-based grouping in 8.2.0 is the kind of operation that is tedious to write directly against the XML.
The case against it is worth stating plainly. A crawler-based sitemap reflects your navigation, so a page that is deliberately unlinked and undocumented will not appear, and a page you no longer want indexed but still want reachable will. Both need the manual escape hatch, which the `getSitemap()` plus `add()` path provides.
The project has 2,622 stars and 294 forks with zero open issues, and the last push to `main` was 2026-08-07, three weeks before the 8.2.0 release. For a package whose failure mode is a malformed file that search engines quietly ignore, that combination of adoption and a clean issue tracker is the relevant signal.
Editorial conclusion
The choice this package makes for you is whether a sitemap is a generated artifact or a declared one, and it quietly supports both. The crawl path is the fastest route to a complete sitemap on a site that already exposes every page, while the `Sitemapable` interface is the right answer when your important URLs come from a database and a crawler would need to reach them through pagination or filters. The feature added in 8.2.0, grouping one crawl into several files behind a closure, is the one that changes how you should think about it, since per-section files in Search Console make coverage gaps visible. Documentation lives on spatie.be rather than in the README, and the README itself carries a funding appeal between the code samples and the docs link. Start with the one-line generator, then read the splitting section before you have more than one section worth tracking.
Frequently asked questions
What is the purpose of a sitemap?
It tells search engines which URLs exist on your site and when they last changed, so pages get discovered and crawled more reliably than by following links alone. A sitemap does not affect ranking directly, and it does not override a noindex directive. In laravel-sitemap the file is produced either by crawling your site or by adding URLs and models yourself.
Is Laravel a frontend or backend framework?
Primarily backend. Laravel is a PHP framework for server-side work such as routing, database access, queues and authentication, and it ships a Blade templating layer for the frontend. This package is squarely on the backend side, generating XML files on the server rather than rendering anything in the browser.
How do I split a sitemap crawl into several files?
Pass a closure to `writeToFile` that receives each `Url` and returns the file path for it. Use `sitemapIndexPath` if you also want an index file listing what was written. Adding `maxTagsPerSitemap()` splits any group that exceeds the limit into numbered files, each listed in that index.
Does laravel-sitemap require any PHP version?
Version 8 does. The 8.0.1 release upgraded to crawler v9, Pest v4 and PHP 8.4 or newer, so upgrading from 7.x means moving to a newer PHP runtime first. The package also brought those test and crawl dependencies forward in the same step.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/spatie-laravel-sitemap)