Why Filament Smart SEO showed [object Object] in the title field

Short answer: The SEO title showed [object Object] because seo_metadata.title is stored as Spatie translatable JSON ({"en":"..."}), and the single-locale Filament field was bound to that array instead of the English string.

This happened on aviwebsquad.in after replacing inline meta_title / og_title columns with Filament Smart SEO.

What the plugin actually stores

seo_metadata is a polymorphic row (seoble_type + seoble_id) with:

HasSeo exposes seo() as morphOne. Reading $record->seo->title in PHP returns the current locale string. Filament relationship state uses the array form, so a plain TextInput::make('title') received {"en":"Maiores in placeat."} and the browser printed [object Object].

Why I did not switch on locale tabs

The plugin's ->translatable() mode uses title.en fields and hides the live SERP preview in the single-locale layout we wanted. The site is English-only (available_locales = en).

The fix is a thin wrapper, SmartSeoSection, that:

  1. On fill, flattens {"en":"..."} to the string for the current locale.
  2. On save/create, wraps the string back into {"en": ...} so Spatie stays valid.

Blog posts, pages, and categories all use that wrapper. Robots and canonical stay on the parent row because Smart SEO does not model them, and sitemap exclusion depends on robots containing noindex.

What I kept out of the plugin

Still on the model Why
robots scopeIndexable() and sitemap skip noindex
canonical_url Per-URL overrides for legal and blog pages

Open Graph and Twitter titles are derived from the Smart SEO title. Featured media still falls back when og_image is empty.

How I confirmed it

A Livewire test on the page edit screen asserts the form state is the plain string Maiores in placeat. and that saving writes {"en":"New SEO title"} back to seo_metadata.

FAQ

Does this break MCP?

MCP still accepts seo.title / seo.description, plus the old meta_title / meta_description aliases. Those write translations for the app locale.

Should I enable Gemini autofill?

Only with GEMINI_API_KEY. The old default model name gemini-2.5-flash is rejected for new API users; this project defaults to gemini-3.6-flash.

Related

See how posts are scheduled without a cron publish job and the Aviwebsquad CMS build log.