Search by

kfoobar / laravel-seo-tools

KFoobar

Breadcrumbs, robots.txt, and sitemaps for Laravel — a companion to laravel/head

Package info

github.com/KFoobar/laravel-seo-tools

pkg:composer/kfoobar/laravel-seo-tools

Statistics

Installs: 2

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.1 2026-10-05 13:17 UTC

This package is auto-updated.

Last update: 2026-10-05 13:20:57 UTC


README

Breadcrumbs, robots.txt, and XML sitemaps for Laravel. Meta tags, Open Graph, and JSON-LD stay in laravel/head.

Installation

composer require kfoobar/laravel-seo-tools

The package requires PHP 8.3+, Laravel 13.17+, and laravel/head.

Publish the config (optional):

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

Publish the breadcrumb view (optional):

php artisan vendor:publish --tag=seo-views

Layout

<head>
    @head
</head>
<body>
    <x-seo::breadcrumbs />

/robots.txt and /sitemap.xml are registered automatically.

Breadcrumbs

Add crumbs in a controller (or anywhere before @head renders):

use KFoobar\LaravelSeoTools\Facades\Breadcrumbs;

Breadcrumbs::add('Home', route('home'))
    ->add('Blog', route('posts.index'))
    ->add($post->title);

The last crumb is the current page: it is not linked, and it gets aria-current="page". The same trail is pushed to Head::schema(Schema::breadcrumbs()) so JSON-LD is rendered by @head without duplicating data.

If the trail is empty, neither the nav nor the schema is rendered.

robots.txt

Configured in config/seo.php, not per request:

'robots' => [
    'enabled' => true,
    'user_agents' => [
        '*' => [
            'allow' => ['/'],
            'disallow' => ['/admin', '/horizon', '/telescope'],
        ],
    ],
    'sitemap' => true, // Sitemap: {app.url}/sitemap.xml
],

This is the site-wide crawl file. Page-level meta robots still belongs to Laravel Head.

Sitemap

No crawler. URLs and models are read from config on each request, so the list stays the same under PHP-FPM and Octane. Each entry is a URL plus optional lastmod. Google ignores changefreq and priority.

'sitemap' => [
    'enabled' => true,
    'route' => '/sitemap.xml',
    'cache' => 3600, // seconds; false to disable. The cache key includes model counts and lastmod.
    'urls' => [
        '/',
        ['loc' => '/contact', 'lastmod' => '2026-03-01'],
    ],
    'models' => [
        App\Models\Post::class,
    ],
],
use KFoobar\LaravelSeoTools\Contracts\Sitemapable;

class Post extends Model implements Sitemapable
{
    public function toSitemapTag(): string|array
    {
        return [
            'loc' => route('posts.show', $this),
            'lastmod' => $this->updated_at,
        ];
    }
}

toSitemapTag() may also return a URL string. Models are read with lazy(), so the full table is not loaded at once.

Set enabled to false before routes are cached if the application serves its own /sitemap.xml or /robots.txt. Rebuild the route cache after changing that flag.

Testing

composer test