Seoquent
#Seoquent
All-in-one SEO for Laravel 10+. One line in your controller, one directive in your blade — done.
#The Problem
SEO in Laravel is a mess. You end up with:
- Meta tags scattered across every Blade view
- Open Graph and Twitter Cards copy-pasted and forgotten
- No structured data (JSON-LD), so Google shows plain blue links instead of rich results
- Sitemap and robots.txt managed manually or with separate packages
- Every model needs its own SEO logic, duplicated everywhere
You spend more time wiring SEO tags than building your actual app.
#The Solution
Seoquent handles everything from one place. Set your SEO in the controller, drop one directive in your layout — the package generates all the meta tags, Open Graph, Twitter Cards, JSON-LD, sitemap, and robots.txt automatically.
// Controller Seo::title('My Post')->description('...')->image('...');
<!-- Layout --> <head> @seoHead </head>
That's it. No more scattered tags. No more forgetting Open Graph. No more missing structured data.
#Features
- Meta Tags — title, description, keywords, robots, canonical, author
- Open Graph — auto-generated from meta tags, fully customizable
- Twitter Cards — auto-generated from Open Graph, fully customizable
- JSON-LD Structured Data — Article, Product, FAQ, Breadcrumbs, Organization, LocalBusiness, and custom schemas
- XML Sitemap — auto-generated from static URLs and Eloquent models, cached, served at
/sitemap.xml - Dynamic robots.txt — configurable allow/disallow rules, served at
/robots.txt - Per-Model SEO —
HasSeotrait to store and load SEO data from the database for any model - Blade Directives —
@seoHeadand@seoJsonLd, zero config rendering - Smart Fallbacks — Twitter falls back to Open Graph, Open Graph falls back to meta tags
- Fluent API — chainable, readable, easy to use
- Laravel 10, 11, 12 — fully compatible
#Install
composer require ibrahim-eng12/seoquent
#3 Steps to Full SEO
#1. Set SEO in your controller
use IbrahimEng12\Seoquent\Facades\Seo; public function show(Post $post) { Seo::title($post->title) ->description($post->excerpt) ->image($post->cover_image); return view('posts.show', compact('post')); }
#2. Add the directive to your layout
<head> @seoHead </head>
#3. That's it. This is what gets rendered:
<title>My Post Title | App Name</title> <meta name="description" content="Post excerpt here..."> <meta name="robots" content="index, follow"> <link rel="canonical" href="https://yoursite.com/posts/my-post"> <meta property="og:title" content="My Post Title"> <meta property="og:description" content="Post excerpt here..."> <meta property="og:type" content="website"> <meta property="og:url" content="https://yoursite.com/posts/my-post"> <meta property="og:image" content="https://yoursite.com/cover.jpg"> <meta name="twitter:card" content="summary_large_image"> <meta name="twitter:title" content="My Post Title"> <meta name="twitter:description" content="Post excerpt here..."> <meta name="twitter:image" content="https://yoursite.com/cover.jpg">
All from 3 lines of code.
#What's Included
| Feature | Auto-generated | Route |
|---|---|---|
| Meta tags (title, description, robots, canonical) | Yes | - |
| Open Graph tags | Yes (falls back to meta) | - |
| Twitter Cards | Yes (falls back to meta) | - |
| JSON-LD structured data | Yes | - |
| XML Sitemap | Yes | /sitemap.xml |
| Dynamic robots.txt | Yes | /robots.txt |
| Per-model SEO (database) | Optional | - |
#API Reference
#Basic SEO
Seo::title('Page Title'); Seo::description('Page description'); Seo::keywords(['keyword1', 'keyword2']); Seo::image('https://example.com/image.jpg'); Seo::canonical('https://example.com/page'); Seo::author('Ibrahim'); Seo::robots('index, follow'); Seo::noIndex();
All methods are chainable:
Seo::title('About Us') ->description('Learn more about our company') ->keywords('company, about, team') ->image('https://example.com/about.jpg') ->author('Ibrahim');
#Title Customization
// Default output: "Page Title | App Name" Seo::title('Page Title'); // Remove suffix: "Page Title" Seo::meta()->withoutTitleSuffix(); // Change separator: "Page Title - App Name" Seo::meta()->titleSeparator(' - ');
#Open Graph
Falls back to your meta title/description automatically. Override when needed:
Seo::openGraph() ->title('Custom OG Title') ->description('Custom OG Description') ->type('article') ->image('https://example.com/og.jpg', 1200, 630, 'Alt text') ->siteName('My Site') ->locale('en_US');
#Twitter Cards
Falls back to Open Graph, then meta tags. Override when needed:
Seo::twitter() ->card('summary_large_image') ->site('@mysite') ->creator('@author') ->title('Custom Twitter Title') ->image('https://example.com/twitter.jpg', 'Alt text');
#JSON-LD Structured Data
Add @seoJsonLd to your layout (before </body>), then use any of these:
// Article Seo::jsonLd()->article( title: 'My Article', author: 'Ibrahim', datePublished: '2025-01-01', dateModified: '2025-01-15', image: 'https://example.com/article.jpg', ); // Organization Seo::jsonLd()->organization('Company Name', 'https://example.com', 'https://example.com/logo.png'); // Breadcrumbs Seo::jsonLd()->breadcrumbs([ ['name' => 'Home', 'url' => '/'], ['name' => 'Blog', 'url' => '/blog'], ['name' => 'Current Post'], ]); // FAQ Seo::jsonLd()->faq([ ['question' => 'What is this?', 'answer' => 'A Laravel SEO package.'], ['question' => 'Is it free?', 'answer' => 'Yes, MIT licensed.'], ]); // Product Seo::jsonLd()->product( name: 'Product Name', description: 'Description', brand: 'Brand', price: 29.99, currency: 'USD', availability: 'InStock', ); // Local Business Seo::jsonLd()->localBusiness( name: 'My Business', address: '123 Main St', phone: '+1234567890', ); // Website with search action Seo::jsonLd()->website('My Site', 'https://example.com', 'https://example.com/search?q='); // Any custom schema Seo::jsonLd()->addSchema([ '@context' => 'https://schema.org', '@type' => 'Event', 'name' => 'My Event', 'startDate' => '2025-06-01', ]);
#XML Sitemap
Register URLs in a service provider's boot() method:
use IbrahimEng12\Seoquent\Facades\Seo; // Static pages Seo::sitemap() ->add(url('/'), now()->toIso8601String(), 'daily', 1.0) ->add(url('/about'), null, 'monthly', 0.8); // From Eloquent models (auto-generates URLs) Seo::sitemap()->addModel( modelClass: \App\Models\Post::class, routeName: 'posts.show', routeKey: 'slug', changefreq: 'weekly', priority: 0.8, queryCallback: fn ($query) => $query->where('published', true), );
Served at /sitemap.xml. Cached by default.
#Per-Model SEO (Database)
Publish the migration first:
php artisan vendor:publish --tag=seoquent-migrations
php artisan migrate
Add the trait to your model:
use IbrahimEng12\Seoquent\Traits\HasSeo; class Post extends Model { use HasSeo; }
Save SEO data:
$post->setSeo([ 'title' => 'Custom SEO Title', 'description' => 'Custom description', 'og_image' => 'https://example.com/image.jpg', 'keywords' => ['laravel', 'seo'], ]);
Apply it in your controller:
public function show(Post $post) { $post->applySeo(); // loads all stored SEO data into Seoquent return view('posts.show', compact('post')); }
Delete SEO data:
$post->deleteSeo();
#Blade Directives
@seoHead {{-- meta + open graph + twitter cards --}}
@seoJsonLd {{-- JSON-LD structured data --}}
Or use Blade components:
<x-seoquent::meta /> <x-seoquent::jsonld />
#Configuration
php artisan vendor:publish --tag=seoquent-config
All defaults can be set in config/seoquent.php:
'defaults' => [ 'title' => env('APP_NAME', 'Laravel'), 'title_separator' => ' | ', 'title_suffix' => env('APP_NAME', 'Laravel'), 'description' => '', 'robots' => 'index, follow', ], 'open_graph' => [ 'type' => 'website', 'site_name' => env('APP_NAME'), 'image' => null, // default OG image for all pages ], 'twitter' => [ 'card' => 'summary_large_image', 'site' => null, // @username 'creator' => null, // @username ], 'sitemap' => [ 'enabled' => true, 'cache_enabled' => true, 'cache_duration' => 3600, ], 'robots' => [ 'enabled' => true, 'allow' => ['/'], 'disallow' => [], ], 'database' => [ 'enabled' => true, 'table_name' => 'seo_meta', ],
#Publishing
php artisan vendor:publish --tag=seoquent-config # Config php artisan vendor:publish --tag=seoquent-migrations # Migration php artisan vendor:publish --tag=seoquent-views # Blade views
#Requirements
- PHP 8.1+
- Laravel 10, 11, or 12
#License
MIT - See LICENSE