Weave 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

Filament Seo Laravel Package

34ml/filament-seo

Filament SEO adds SEO fields and management to Filament resources, letting you edit meta titles, descriptions, Open Graph data, and canonical URLs directly from the admin panel. Laravel-friendly and configurable, it keeps SEO tasks close to your content workflow.

View on GitHub
Deep Wiki
Context7

Getting Started

This package wires romanlazko/laravel-seo (the polymorphic SEO meta model) into Filament, so a resource's edit form gains a ready-made SEO block (meta title, description, canonical, robots, OpenGraph, etc.) without hand-rolling the relation.

Minimal steps for v3:

  1. composer require 34ml/filament-seo:^3.0 — this release requires Filament v5. If the panel is still on v3/v4, stay on 2.3.x.
  2. Publish and run the underlying laravel-seo migration (the seo table) if you haven't already; then republish this package's assets/config per the README.
  3. Add HasSEO (from laravel-seo) to the model you want to describe — e.g. Post, Page, Product.
  4. Drop the package's SEO component into the resource's form() schema, typically at the end of the main column or inside a dedicated Tabs tab.

First use case to prove it out: take one existing resource (PageResource), add the SEO fieldset to form(), open an edit page, fill title/description, save, and confirm a row lands in seo with the correct model_type/model_id. Then render it in Blade ($page->seo->title) before doing the same rollout across other resources.

Read the README first for the exact component class name/namespace in v3, then the laravel-seo docs for how the meta is consumed on the front end — that split (Filament side vs. Eloquent side) is the main mental model you need.

Implementation Patterns

Standard resource wiring. Keep the SEO block in a separate tab (Tabs\Tab::make('SEO')) so editors don't trip over it on every save. The component binds to the model's seo relation; you generally don't need to add ->relationship('seo') yourself, but if you extract a shared schema closure make sure the owning record exists before the fields hydrate (it does on edit pages, not on create).

Shared schema across resources. Put the SEO fields in a static method on a small helper class (SeoForm::schema()) and call it from every resource that has HasSEO. This keeps the block consistent when you upgrade the package — one place to change when a field is renamed.

Localisation. laravel-seo stores translations per locale in the meta payload. If you run a multi-locale panel, wrap the SEO tab in Filament's locale switcher workflow or render one SEO block per locale explicitly; don't assume the fieldset follows the panel locale automatically — verify what the saved seo row looks like per locale before shipping.

Programmatic filling. Seed defaults on create (->default(fn ($record) => $record?->title)), or in an observer/afterCreate hook, so a record that never gets touched by an editor still has a sane meta title. Good pattern: a SeoDefaults service that generates title/description from the model's own fields and only applies when the meta field is empty.

Table columns and N+1. Showing a "has SEO" badge or meta title in a table() column means a polymorphic seo relation per row. Eager load it (->modifyQueryUsing(fn ($q) => $q->with('seo'))) or use an aggregate/exists query. Without this, a 50-row index fires 50 extra queries.

Front-end integration. Do the rendering outside Filament — a Blade component or an SEO manager that reads $model->seo and falls back to model defaults. Keep that fallback logic in one place so the Filament form and the rendered <head> never disagree.

Permissions. The SEO tab is content, not configuration — but if marketing owns it, gate it with ->visible(fn () => auth()->user()->can('manageSeo', $record)) rather than hiding fields inside the schema.

Gotchas and Tips

  • v3 is a Filament v5 jump. Alongside the laravel-seo version bump, expect Filament's own v4→v5 breaking changes in how form components, actions, and asset registration are declared. Budget a real upgrade slot: bump Filament first, get the panel booting, then add filament-seo:^3.0. Upgrading both at once makes failures ambiguous.
  • The laravel-seo bump can mean migration drift. Diff the published seo table migration against what's in your DB before running migrate in production; a changed column type or new index on a large table can lock.
  • Missing trait is the #1 error. If the component renders but saving throws a relation/BadMethodCallException, the model is missing HasSEO or the relation name doesn't match what the package expects. Check the trait before debugging the form.
  • Not every save path uses the form. Imports, API endpoints, and console commands bypass your Filament schema, so those records have no seo row. Your front-end fallback logic must tolerate $model->seo === null.
  • Polymorphic orphans. Deleting a record via raw SQL or a delete() that skips model events leaves seo rows behind. Add a deleting observer on SEO-enabled models, or clean up periodically.
  • OG image handling. If the fieldset offers an image upload, confirm the disk and directory are configured (not the default public disk) before editors upload production assets — moving them later means rewriting stored paths.
  • Caching. Sitemap/meta caching layers won't be invalidated by a Filament save automatically. Hook saved on SEO-enabled models to flush whatever cache key your front end uses.
  • Extension point for custom meta. Extra head tags (hreflang, schema.org JSON-LD) usually belong in your own front-end renderer, not by adding fields to the package's schema — customising the vendor fieldset makes future upgrades painful.
  • Debugging tip: when the tab "doesn't save", dd($record->seo()->first()) immediately after save in a tinker session. If the row is written there, the bug is in your front-end read; if not, it's the form's relationship wiring.
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.
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect
youbar/easy-crud
c-point-labs/laravel-hostyourai
dbravoan/dbcms-admin-theme-dbcms
dbravoan/dbcms-theme-db-cyber
dbravoan/dbcms-blocks-basic
dbravoan/dbcms-module-content-audit
dbravoan/dbcms-module-ai-assistant
paygate-to/paygate-php
padosoft/askmydocs-connector-base
defstudio/supercharged-carbon
bernskioldmedia/laravel-matomo