Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Seo Laravel Package

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.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require ralphjsmit/laravel-seo
    php artisan vendor:publish --tag="seo-migrations"
    php artisan vendor:publish --tag="seo-config"
    php artisan migrate
    
  2. Configure: Update config/seo.php with your site’s site_name, favicon, and other defaults (e.g., title.suffix).

  3. 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;
    }
    

    Include the SEO tags in your Blade layout:

    <head>
        {!! seo()->for($post) !!}
    </head>
    

Implementation Patterns

Core Workflows

  1. Static SEO for Pages: Use getDynamicSEOData() to dynamically generate SEO metadata from model attributes:

    public function getDynamicSEOData(): SEOData
    {
        return new SEOData(
            title: $this->title,
            description: $this->excerpt,
            image: $this->featuredImagePath,
        );
    }
    

    Call it in Blade:

    {!! seo()->for($post) !!}
    
  2. Manual Overrides: Override SEO data via the associated seo model:

    $post->seo->update([
        'title' => 'Updated Title',
        'robots' => 'noindex',
    ]);
    
  3. Controller-Driven SEO: Pass SEOData directly from a controller:

    return view('page', [
        'SEOData' => new SEOData(
            title: 'Custom Title',
            description: 'Custom Description',
        ),
    ]);
    

    Render in Blade:

    {!! seo($SEOData) !!}
    
  4. Structured Data: Use SchemaCollection for JSON-LD (e.g., FAQ, Article):

    use RalphJSmit\Laravel\SEO\Support\Schema\Article;
    
    public function getDynamicSEOData(): SEOData
    {
        return new SEOData(
            schema: new SchemaCollection([
                new Article(
                    headline: $this->title,
                    description: $this->excerpt,
                    datePublished: $this->published_at,
                ),
            ]),
        );
    }
    
  5. Fallbacks: Leverage config fallbacks (e.g., description.fallback, image.fallback) for pages without explicit SEO data.


Integration Tips

  • Dynamic Routes: Use title.infer_title_from_url to auto-generate titles from URLs (e.g., /blog/post-namePost Name).
  • Multilingual Sites: Use AlternateTag for hreflang links:
    new SEOData(
        alternates: [
            new AlternateTag(hreflang: 'en', href: route('post.en', $post)),
            new AlternateTag(hreflang: 'fr', href: route('post.fr', $post)),
        ],
    )
    
  • Conditional Rendering: Skip SEO tags for non-critical pages (e.g., admin routes):
    @unless(request()->is('admin*'))
        {!! seo()->for($post) !!}
    @endunless
    
  • Testing: Mock SEOData in tests:
    $this->view->share('SEOData', new SEOData(title: 'Test Title'));
    

Gotchas and Tips

Pitfalls

  1. Image Paths: Ensure image paths in SEOData are relative to public_path() (e.g., 'images/post.jpg'). Absolute paths or incorrect paths will break OpenGraph/Twitter cards.

    // Correct:
    image: 'images/posts/1.jpg'
    // Incorrect (will fail):
    image: 'https://example.com/images/1.jpg'
    
  2. Title Suffix Conflicts: The title.suffix in config is appended globally. Override it per-page with enableTitleSuffix: false in SEOData:

    new SEOData(enableTitleSuffix: false)
    
  3. Robots Tag Overrides: If robots.force_default is true, manual overrides (e.g., $seo->robots = 'noindex') will fail. Set force_default: false in config to allow overrides.

  4. Schema Validation: Invalid JSON-LD (e.g., missing required fields) will render as malformed HTML. Validate schemas using Google’s Rich Results Test.

  5. Caching: SEO data is not cached by default. For dynamic pages, cache SEOData in the controller or use Laravel’s cache middleware:

    $seoData = Cache::remember("seo_{$post->id}", 60, fn() => $post->getDynamicSEOData());
    
  6. Alternate Links: Ensure alternates URLs are absolute (e.g., https://example.com/en). Relative URLs may break hreflang validation.


Debugging Tips

  1. Inspect Output: Use browser dev tools to verify rendered tags:

    <title>Page Title</title>
    <meta property="og:title" content="Page Title">
    

    Check for missing or malformed tags (e.g., empty og:image).

  2. Log SEOData: Add a helper to log SEOData for debugging:

    if (app()->environment('local')) {
        \Log::debug('SEOData', $post->getDynamicSEOData()->toArray());
    }
    
  3. Validate Structured Data: Use Google’s Rich Results Test to validate JSON-LD.

  4. Check Config Overrides: If tags aren’t rendering, verify:

    • The seo() helper is called in Blade.
    • The model has HasSEO and a seo relationship.
    • No config values (e.g., site_name) are null.

Extension Points

  1. Custom Transformers: Override SEO data globally using a closure in the SEOManager:

    use RalphJSmit\Laravel\SEO\Facades\SEO;
    
    SEO::SEODataTransformer(function ($data) {
        $data->title .= ' | Custom Suffix';
        return $data;
    });
    
  2. Custom Schema Types: Extend SchemaCollection for new JSON-LD types (e.g., Product):

    namespace App\SEO;
    
    use RalphJSmit\Laravel\SEO\Support\Schema\Schema;
    
    class Product extends Schema
    {
        public function __construct(
            public string $name,
            public float $price,
        ) {}
    }
    
  3. Dynamic Favicon: Override the favicon config dynamically:

    config(['seo.favicon' => $dynamicFaviconPath]);
    
  4. Event Listeners: Trigger actions when SEO data changes (e.g., update sitemap):

    $post->seo->updated(function ($seo) {
        \Spatie\Sitemap\SitemapGenerator::create()->write();
    });
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
codraw/framework-extra-bundle
codraw/messenger
codraw/security
codraw/mailer
codraw/contracts
codraw/profiling
codraw/dependency-injection
codraw/tester
codraw/core
nexmo/api-specification
capell-app/block-library
axium/identity
cetria/laravel-dummy-models
cetria/reflection-helper
agropredict/sso-auth-bundle
evolvestudio/spam-protection
datacore/hub-sdk
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony