ralphjsmit/laravel-seo
Laravel SEO made easy: generates valid meta tags out of the box (title, meta, OpenGraph, Twitter, structured data, favicon, robots, alternates). Store SEO per model, render with seo()->for($model), or provide dynamic SEOData without saving.
Installation:
composer require ralphjsmit/laravel-seo
php artisan vendor:publish --tag="seo-migrations"
php artisan vendor:publish --tag="seo-config"
php artisan migrate
Configure:
Update config/seo.php with your site’s metadata (e.g., site_name, favicon, robots defaults).
First Use Case:
Add the HasSEO trait to a model (e.g., Post):
use RalphJSmit\Laravel\SEO\Support\HasSEO;
class Post extends Model
{
use HasSEO;
}
Render SEO tags in Blade:
{!! seo()->for($post) !!}
Model Association:
HasSEO to Eloquent models to auto-create SEO records.$model->seo to access/update SEO data:
$post->seo->update(['title' => 'Updated Title']);
Dynamic SEO Data:
getDynamicSEOData() to fetch SEO fields dynamically:
public function getDynamicSEOData(): SEOData
{
return new SEOData(
title: $this->title,
description: $this->excerpt,
image: $this->featuredImagePath,
);
}
Controller Integration:
SEOData directly from controllers:
return view('page', [
'SEOData' => new SEOData(title: 'Custom Title'),
]);
Blade Rendering:
seo()->for($model) or seo($SEOData) in layouts.Structured Data:
$schema = new SchemaCollection([
new ArticleSchema(
headline: $post->title,
datePublished: $post->published_at,
),
]);
return new SEOData(schema: $schema);
Locale Handling:
return new SEOData(locale: 'fr', alternates: [new AlternateTag(hreflang: 'fr', href: route('post.fr', $post))]);
Conditional SEO:
if ($post->isDraft()) {
return new SEOData(robots: 'noindex');
}
Image Paths:
image paths in SEOData are relative to public_path() (e.g., 'images/post.jpg').secure_url($path) to verify accessibility.Title Suffix:
title suffix (from config/seo.php) is not applied to homepage unless manually added.Robots Tag:
robots.force_default is true, manual overrides via SEOData are ignored.Dynamic Data Priority:
getDynamicSEOData() overrides static SEO model data. Use SEODataTransformer for global overrides:
SEOManager::SEODataTransformer(fn ($data) => $data->markAsNoIndex());
dd(seo()->for($post)->render());
php artisan config:clear if changes to seo.php aren’t reflected.Custom Schema:
Extend SchemaCollection or create new schema classes (e.g., FAQSchema).
SEODataTransformer: Modify SEO data globally via closures:
SEOManager::SEODataTransformer(fn ($data) => $data->setSiteName('My Site'));
Fallback Logic:
Override SEOManager::getFallbackSEOData() for custom defaults.
Alternate Tags:
Use AlternateTag for hreflang support:
new SEOData(alternates: [new AlternateTag(hreflang: 'es', href: route('post.es', $post))]);
How can I help you explore Laravel packages today?