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:
title,description,keywordsas JSON translationsog_imageas a path string
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:
- On fill, flattens
{"en":"..."}to the string for the current locale. - 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.