jegex / laravel-seo
This is my package laravel-seo
Fund package maintenance!
Requires
- php: ^8.4
- illuminate/contracts: ^11.0||^12.0||^13.0
- spatie/laravel-package-tools: ^1.16
Requires (Dev)
- larastan/larastan: ^3.0
- laravel/pint: ^1.14
- nunomaduro/collision: ^8.8
- orchestra/testbench: ^10.0.0||^9.0.0
- pestphp/pest: ^4.0
- pestphp/pest-plugin-arch: ^4.0
- pestphp/pest-plugin-laravel: ^4.0
- phpstan/extension-installer: ^1.4
- phpstan/phpstan-deprecation-rules: ^2.0
- phpstan/phpstan-phpunit: ^2.0
This package is auto-updated.
Last update: 2026-04-30 15:47:10 UTC
README
A comprehensive SEO package for Laravel inspired by Rank Math SEO (WordPress). Features include meta tags, Open Graph, Twitter Cards, template variables, sitemap generation, redirect management, and 404 tracking.
Note: This is a core SEO package - no admin dashboard included. For a Filament admin dashboard, check out the separate
filament-seopackage.
Support us
We invest a lot of resources into creating best in class open source packages. You can support us by buying one of our paid products.
We highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using. You'll find our address on our contact page. We publish all received postcards on our virtual postcard wall.
Installation
You can install the package via composer:
composer require jegex/laravel-seo
You can publish and run the migrations with:
php artisan vendor:publish --tag="laravel-seo-migrations"
php artisan migrate
You can publish the config file with:
php artisan vendor:publish --tag="laravel-seo-config"
Optionally, you can publish the views using
php artisan vendor:publish --tag="laravel-seo-views"
Quick Start
1. Add Trait to Your Model
use Jegex\LaravelSeo\Traits\HasSeo; class Post extends Model { use HasSeo; }
2. Use in Your Blade Layout
<!DOCTYPE html> <html> <head> {{ seo()->render() }} </head> <body> @yield('content') </body> </html>
3. Configure Templates (config/seo.php)
return [ 'site_name' => 'My Blog', 'site_description' => 'A blog about Laravel', 'separator' => ' - ', 'templates' => [ 'post' => [ 'title' => '%title% %sep% %sitename%', 'description' => '%excerpt%', ], ], 'webmaster_verification' => [ 'google' => 'your-google-verification-code', 'bing' => 'your-bing-verification-code', ], ];
Features
Template Variables (Rank Math Style)
Use these variables in your templates:
| Variable | Description |
|---|---|
%title% |
Model title |
%sitename% |
Website name |
%sitedesc% |
Website description |
%sep% |
Separator (default: -) |
%currentdate% |
Current date |
%currentyear% |
Current year |
%author% |
Author name |
%excerpt% |
Content excerpt (160 chars) |
%categories% |
Categories list |
%tags% |
Tags list |
Helper Functions
// Set SEO data seo('title', 'My Page Title'); seo('description', 'Page description'); seo('og:image', '/path/to/image.jpg'); // Or use methods seo()->setTitle('My Title')->setDescription('My Description'); // Parse templates manually parse_seo_template('%title% %sep% %sitename%', ['title' => 'Hello']);
Blade Components
<x-seo::meta-tags /> <x-seo::json-ld :schema="['@type' => 'Article', 'headline' => $title]" /> <x-seo::breadcrumbs :items="[['name' => 'Home', 'url' => '/'], ['name' => $category]]" />
Webmaster Verification
Add verification codes in config/seo.php:
'webmaster_verification' => [ 'google' => 'abc123...', 'bing' => 'xyz789...', 'pinterest' => 'pinterest-code', 'yandex' => 'yandex-code', ],
Redirect Management
use Jegex\LaravelSeo\Models\Redirect; // Create a 301 redirect Redirect::create([ 'from_url' => '/old-page', 'to_url' => '/new-page', 'type' => 301, ]); // Create a regex redirect Redirect::create([ 'from_url' => '#/blog/(.*)#', 'to_url' => '/articles/$1', 'type' => 301, 'is_regex' => true, ]); // 410 Gone Redirect::create([ 'from_url' => '/deleted-page', 'type' => 410, ]);
Middleware
Add to your app/Http/Kernel.php:
protected $middleware = [ // ... \Jegex\LaravelSeo\Http\Middleware\RedirectMiddleware::class, \Jegex\LaravelSeo\Http\Middleware\NotFoundTrackerMiddleware::class, ];
SEO Analysis
CLI Command
# Analyze all SEO entries php artisan seo:analyze # Analyze specific model php artisan seo:analyze --model="App\Models\Post" # Calculate and save scores php artisan seo:analyze --calculate-scores
Model Analysis
$entry = $post->seoEntry; // Get full analysis $analysis = $entry->analyze(); // [ // 'score' => 85, // 'alerts' => ['Title is slightly long...'], // 'checks' => [...], // 'details' => [...] // ] // Get score only $entry->calculateScore(); echo $entry->seo_score; // 85 // Get score label echo $entry->getScoreLabel(); // 'Good', 'Needs Improvement', or 'Poor'
Programmatic Analysis
use Jegex\LaravelSeo\Services\AnalyzerService; $analyzer = app(AnalyzerService::class); $analysis = $analyzer->analyze($content, [ 'title' => 'My Title', 'description' => 'My Description', 'focus_keyword' => 'laravel seo', ]); echo $analysis['score']; // 0-100 echo $analysis['alerts'][0]; // First alert message
Breadcrumbs
Via Service
// Manual breadcrumbs seo()->breadcrumbs() ->add('Home', '/') ->add('Blog', '/blog') ->add($post->title, $post->url()); // Auto from route seo()->breadcrumbs()->fromRoute(); // Render {{ seo()->breadcrumbs()->renderHtml() }} {{ seo()->breadcrumbs()->renderSchema() }}
Via Blade Component
<x-seo::breadcrumbs :items="[ ['name' => 'Home', 'url' => '/'], ['name' => 'Blog', 'url' => '/blog'], ['name' => $post->title] ]" />
JSON-LD Structured Data
Available Schema Types
article- Article/BlogPostingwebsite- WebSite with SearchActionorganization- Organization with contact infobreadcrumbs- BreadcrumbList
Creating Schema
// Via SEO service (auto-rendered) seo()->addSchema('article') ->headline('My Article') ->author('John Doe') ->datePublished(now()->toIso8601String()) ->image('https://example.com/image.jpg'); // Via Schema service seo()->schema() ->website() ->name('My Site') ->url('/') ->potentialActionSearch('/search?q={search_term_string}'); // Organization schema seo()->schema() ->organization() ->name('My Company') ->url('https://example.com') ->contactPoint('+1-234-567-8900', 'customer service');
Rendering
{{-- In your layout, schemas auto-render with meta tags --}} {{ seo()->render() }} {{-- Or render schemas only --}} {{ seo()->schema()->render() }} {{-- Manual schema --}} <x-seo::json-ld :schema="['@type' => 'Article', 'headline' => $title]" />
Complete Example
Controller
class PostController extends Controller { public function show(Post $post) { // Automatically uses HasSeo trait for defaults seo()->for($post); // Or manually override seo()->setTitle($post->title . ' | Custom Suffix'); return view('posts.show', compact('post')); } }
View (posts/show.blade.php)
@extends('layouts.app') @section('content') <article> <h1>{{ $post->title }}</h1> <div>{{ $post->content }}</div> </article> @endsection
Layout (layouts/app.blade.php)
<!DOCTYPE html> <html lang="{{ app()->getLocale() }}"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> {{-- Render all SEO meta tags --}} {{ seo()->render() }} @stack('styles') </head> <body> @yield('content') @stack('scripts') </body> </html>
Testing
composer test
Changelog
Please see CHANGELOG for more information on what has changed recently.
Contributing
Please see CONTRIBUTING for details.
Security Vulnerabilities
Please review our security policy on how to report security vulnerabilities.
Credits
License
The MIT License (MIT). Please see License File for more information.