murdercode/nova4-seo-description
A Laravel Nova 4 field for adding and editing SEO meta descriptions directly in your resources, helping you keep search snippets concise and manage SEO content without leaving the admin panel.
This package contributes one thing: a Nova 4 field type that renders an SEO-meta-description input with client-side affordances (character counter and over-limit warning; some builds also show a Google-style SERP preview). It is a field, not a full SEO suite — it gives you one well-behaved text input, nothing more.
Minimal path:
composer require murdercode/nova4-seo-description
vendor/murdercode/nova4-seo-description/src/ (or the README) and check the field class and namespace (expected to be under Murdercode\Nova4SeoDescription).// use Murdercode\Nova4SeoDescription\SeoDescription; // verify class name
public function fields(NovaRequest $request)
{
return [
Text::make('Title', 'title'),
SeoDescription::make('Meta Description', 'meta_description')
->rules('nullable', 'string', 'max:160'),
];
}
$table->string('meta_description', 160)->nullable(); (or text() if you allow long descriptions and clip for display only).php artisan nova:publish, then hard-refresh. If the package ships compiled JS under dist/, this is the usual fix.Where to look first: README → the field's src/*.php (for fluent methods) → resources/js/ (for how the counter and any preview are computed). First use case: drop it beside existing title/slug fields on any resource with a public URL (pages, posts, products) so editors can see the length before saving.
Baseline field + validation parity. The counter is cosmetic; your validation rules are the real guardrail. Keep them in sync:
SeoDescription::make('Meta Description')
->rules('nullable', 'string', 'max:160')
->help('Aim for 120–160 characters.');
Pair with a title field for context. SEO descriptions are written against the title/topic, so keep them adjacent and use ->stacked()/->fullWidth() if the layout feels cramped.
Reuse across resources via a trait or base resource. Since this is one field, wrap the "SEO block" (title, description, slug, OG image) in a trait or a fieldsForSeo() method so you don't reconfigure limits in five resources:
protected function seoFields(): array
{
return [
SeoDescription::make('Meta Description', 'meta_description')
->rules('nullable', 'string', 'max:160'),
];
}
Then merge it wherever the model has an SEO block.
Store on the model or an SEO relation. If you use spatie/laravel-seo or a dedicated seo_meta table already, point this field at the existing attribute/relation column instead of introducing a second description column — otherwise you end up with two "descriptions" that drift.
Server-side trimming vs. hard rejection. Decide one policy and encode it in rules. max:160 rejects; ->rules('string') plus a FormRequest/saving() mutator that Str::limit(..., 160) accepts and trims. Pick one so editors aren't surprised by a red error after typing 170 chars.
Fluent extension (if the field supports it). Many Nova field packages expose ->maxLength() or ->recommendedLength(). If the README mentions one, prefer it over hardcoding max:160 in rules so the counter and validator share a single source of truth — and set the column length to match.
composer.json.string.length counts UTF-16 units and emoji as 2; Laravel's max:160 counts characters via mb_strlen. Your counter and validator can disagree on accented/emoji-heavy copy. If that matters, trim server-side and let the counter be advisory.varchar(160) with max:160 is fine, but a varchar(255) with a max:160 rule means the DB silently permits what Nova rejects. Keep DB, rule, and counter aligned.php artisan nova:publish, npm run prod on your app if you've customized, and hard-refresh.searchable()/sortable() only make sense if the underlying column is indexed; otherwise leave them off rather than exposing a slow sort to editors.recommendedLength, or write a Nova custom field (~one Vue component + one PHP class) — for a single counter, that's often less work than debugging an unmaintained dependency. Fork the repo if you need it maintained; nothing upstream will fix it for you.How can I help you explore Laravel packages today?