Overview
The SEO plugin is a first-party panel plugin that attaches search and social metadata to any Eloquent model. It stores that metadata in a single polymorphic seo_meta table, so one install serves posts, docs, pages, and any future model without a per-table migration.
What ships
FilamentSeoPlugin::make()— the panel plugin object. Registered with->plugin(FilamentSeoPlugin::make())on a panel; adds theSeoSettingspage.HasSeotrait — the model side. Adds aseo(): MorphOnerelation toSeoMetakeyed onseoable_type/seoable_id.SeoFields— a formSectionbound to theseorelationship. Drops in on any Filament form: title (≤70), description (≤160), canonical URL, Open Graph image URL.SeoTags— the head renderer.SeoTags::for($model)reads the morph row, falls back to the model'stitleandconfig('app.name'), and renders<title>, meta description, canonical,og:*, Twitter card, and JSON-LDWebPagevia thefilament-seo::tagsBlade view.Sitemap—Sitemap::xml($urls)returns a valid<urlset>for/sitemap.xml; the clone wires the route.
How a model gets SEO
Use the HasSeo trait on the model, then add SeoFields::make() to its Filament form schema. On save, Filament persists the section through the seo morph relation into seo_meta. On the public page, the clone's Blade calls SeoTags::for($model)->toHtml() and places the result in <head>.
Look-neutral
The plugin renders tags only. It ships no public Blade layout, no theme, no CSS. The clone decides where and how the tags land in <head> — so each product keeps its own look.
Authorization
SEO fields on a resource are Filament form components, so they follow the same gate as the resource. With Shield, the resource's view, create, and update permissions gate who can edit SEO per row; the SeoSettings page is gated by its own Shield permission.
flowchart LR
M[Model with HasSeo] -->|morphOne| R[(seo_meta row)]
R -->|SeoTags::for| T[head tags]
T --> H[<head>: title, meta, canonical, og:*, JSON-LD]
S[Sitemap::xml] --> X[/sitemap.xml/]