magutti / seo-tools
SEO Tools for Laravel
Requires
- php: ^8.2
- ext-json: *
- illuminate/config: ^12.0 || ^13.0
- illuminate/support: ^12.0 || ^13.0
Requires (Dev)
- orchestra/testbench: ^10.0
- phpunit/phpunit: ^11.5
README
A comprehensive SEO package for Laravel 12/13 that manages meta tags, Open Graph, Twitter Cards, and JSON-LD structured data from a single unified interface.
Features
- Meta Tags — title, description, keywords, canonical, robots, hreflang, pagination, webmaster verification
- Open Graph — full OG support including articles, videos, music, books, products, and profiles
- Twitter Cards — summary, large image, app, and player card types
- JSON-LD — Schema.org structured data, single or multiple blocks per page
- Inertia.js integration — reactive meta tags for Vue/React frontends
- Synchronized updates — setting a title once propagates across all formats
Requirements
- PHP ^8.2
- Laravel 12 or 13
Installation
composer require magutti/seo-tools
The service provider and facades are auto-discovered by Laravel.
Publish the configuration file:
php artisan vendor:publish --provider="Magutti\SeoTools\Providers\SeoToolsServiceProvider"
Configuration
config/seotools.php controls defaults for all managers:
return [ 'inertia' => false, // set to true to enable Inertia.js support 'meta' => [ 'defaults' => [ 'title' => false, 'titleBefore' => false, 'description' => false, 'separator' => ' - ', 'keywords' => [], 'canonical' => false, 'robots' => false, ], 'webmaster_tags' => [ // 'google' => 'your-code', // 'bing' => 'your-code', // 'alexa' => 'your-code', // 'pinterest' => 'your-code', // 'yandex' => 'your-code', // 'norton' => 'your-code', ], ], 'opengraph' => [ 'defaults' => [ 'title' => false, 'description' => false, 'url' => false, 'type' => false, 'site_name' => false, 'images' => [], ], ], 'twitter' => ['defaults' => []], 'json-ld' => [ 'defaults' => [ 'title' => false, 'description' => false, 'url' => false, 'type' => 'WebPage', 'images' => [], ], ], ];
Basic Usage
In a controller
use Magutti\SeoTools\Facades\SEOTools as SEO; class ArticleController extends Controller { public function show(Article $article) { SEO::setTitle($article->title) ->setDescription($article->excerpt) ->setCanonical(route('articles.show', $article)) ->addImages([$article->cover_url]); return view('articles.show', compact('article')); } }
In a Blade view
<head> {!! SEO::generate() !!} </head>
Pass true to minify the output:
{!! SEO::generate(true) !!}
Using the trait
use Magutti\SeoTools\Traits\SEOTools; class PostController extends Controller { use SEOTools; public function show(Post $post) { $this->seo()->setTitle($post->title); } }
Facades
| Facade | Alias | Class |
|---|---|---|
SEOTools |
SEO |
Magutti\SeoTools\Facades\SEOTools |
SEOMeta |
SEOMeta |
Magutti\SeoTools\Facades\SEOMeta |
OpenGraph |
OpenGraph |
Magutti\SeoTools\Facades\OpenGraph |
TwitterCard |
Twitter |
Magutti\SeoTools\Facades\TwitterCard |
JsonLd |
JsonLd |
Magutti\SeoTools\Facades\JsonLd |
JsonLdMulti |
— | Magutti\SeoTools\Facades\JsonLdMulti |
SEOMeta
use Magutti\SeoTools\Facades\SEOMeta; SEOMeta::setTitle('Page Title') ->setDescription('Page description') ->setKeywords(['laravel', 'seo']) ->setCanonical('https://example.com/page') ->setRobots('index,follow') ->addAlternateLanguage('it', 'https://example.com/it/page') ->setPrev('https://example.com/page/1') ->setNext('https://example.com/page/3');
Open Graph
use Magutti\SeoTools\Facades\OpenGraph; OpenGraph::setTitle('Page Title') ->setDescription('Page description') ->setUrl('https://example.com/page') ->setType('article') ->setSiteName('My Site') ->addImage('https://example.com/image.jpg'); // Article-specific properties OpenGraph::setArticle([ 'published_time' => '2024-01-01', 'author' => 'Marco Asperti', 'section' => 'Technology', 'tags' => ['laravel', 'seo'], ]);
Supported OG types with dedicated setters: article, profile, book, music.song, music.album, video.movie, video.episode, video.tv_show, product.
Twitter Cards
use Magutti\SeoTools\Facades\TwitterCard; TwitterCard::setType('summary_large_image') ->setSite('@mysitehandle') ->setTitle('Page Title') ->setDescription('Page description') ->setImage('https://example.com/image.jpg');
JSON-LD
Single block
use Magutti\SeoTools\Facades\JsonLd; JsonLd::setType('Article') ->setTitle('Article Title') ->setDescription('Article description') ->setUrl('https://example.com/article') ->addImage('https://example.com/image.jpg') ->addValue('author', ['@type' => 'Person', 'name' => 'Marco Asperti']) ->addValue('datePublished', '2024-01-01');
Multiple blocks
use Magutti\SeoTools\Facades\JsonLdMulti; // First block (index 0 is created automatically) JsonLdMulti::setType('Article')->setTitle('Article Title'); // Add a second block JsonLdMulti::newJsonLd() ->setType('BreadcrumbList') ->addValue('itemListElement', [/* breadcrumb items */]); // Switch between blocks JsonLdMulti::select(0)->setDescription('Updated description');
SEOFriendly Interface
Implement Magutti\SeoTools\Contracts\SEOFriendly on your models to allow controllers to load SEO data in a standardized way:
use Magutti\SeoTools\Contracts\SEOFriendly; use Magutti\SeoTools\Contracts\MetaTags; use Magutti\SeoTools\Contracts\OpenGraph; use Magutti\SeoTools\Contracts\TwitterCards; use Magutti\SeoTools\Contracts\JsonLd; class Article extends Model implements SEOFriendly { public function loadSEO(MetaTags $meta, OpenGraph $og, TwitterCards $twitter, JsonLd $jsonLd): void { $meta->setTitle($this->title)->setDescription($this->excerpt); $og->setTitle($this->title)->addImage($this->cover_url); $twitter->setTitle($this->title)->setImage($this->cover_url); $jsonLd->setType('Article')->setTitle($this->title); } }
Then in your controller:
use Magutti\SeoTools\Traits\SEOTools; class ArticleController extends Controller { use SEOTools; public function show(Article $article) { $this->loadSEO($article); return view('articles.show', compact('article')); } }
Inertia.js Support
Set 'inertia' => true in config/seotools.php (or SEO_TOOLS_INERTIA=true in .env). Generated tags will include the inertia attribute, making them reactive in Vue/React components via @inertiajs/vue3 or @inertiajs/react.
License
MIT — see LICENSE.md.