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

Seo Meta Nova Field Laravel Package

gwd/seo-meta-nova-field

gwd/seo-meta-nova-field adds a ready-to-use SEO metadata field to Laravel Nova. Manage page titles, meta descriptions, keywords, Open Graph and Twitter card tags from a clean Nova form, keeping SEO content alongside your Eloquent models. Reduce custom code and keep metadata editable by non-developer...

View on GitHub
Deep Wiki
Context7

Getting Started

Verify the exact API against the README before wiring anything up — the package is small (24 stars, no dependents) and the docs are the only source of truth for the field class name and expected schema.

  1. Install into an existing Nova app:

    composer require gwd/seo-meta-nova-field
    

    Check for a publish step (php artisan vendor:publish --provider="Gwd\SeoMeta\...") — most Nova fields publish nothing, but confirm before assuming.

  2. Add storage. The field bundles multiple SEO inputs into one Nova field, which almost always means a single JSON column. Confirm the expected column in the README, then:

    Schema::table('pages', function (Blueprint $table) {
        $table->json('seo')->nullable();
    });
    
  3. Cast it so Blade can read it as an array:

    protected $casts = ['seo' => 'array'];
    
  4. Register on the resource:

    use Gwd\SeoMeta\SeoMeta; // confirm namespace from README
    
    public function fields(Request $request)
    {
        return [
            ID::make()->sortable(),
            Text::make('Title'),
            Slug::make('Slug'),
            SeoMeta::make('SEO', 'seo')
                ->hideFromIndex(),
        ];
    }
    

Where to look first: src/ for the field class (tells you the column contract and whether it uses fillUsing/resolveUsing), and resources/js/ for the Vue component (tells you every sub-field it actually renders — title, description, OG image, robots, canonical, etc.). The component is the real documentation.

First use case: a Page or Post resource where an editor sets meta title, meta description, OG image, and robots directives without leaving the Nova form — then you render those values in your Blade <head>.

Implementation Patterns

Render from a head partial. Keep SEO markup in one place and always fall back:

@php($seo = $page->seo ?? [])
<title>{{ $seo['title'] ?? $page->title }}</title>
<meta name="description" content="{{ $seo['description'] ?? Str::limit(strip_tags($page->body), 155) }}">

@if(!empty($seo['og_image']))
    <meta property="og:image" content="{{ Storage::disk('public')->url($seo['og_image']) }}">
@endif

<meta name="robots" content="{{ ($seo['robots'] ?? 'index,follow') }}">

Centralise defaults with an accessor so controllers and views never repeat fallback logic:

public function getSeoMetaAttribute(): array
{
    $seo = $this->seo ?? [];

    return [
        'title'       => $seo['title'] ?? $this->title,
        'description' => $seo['description'] ?? Str::limit(strip_tags($this->body), 155),
        'og_image'    => isset($seo['og_image'])
            ? Storage::disk('public')->url($seo['og_image'])
            : null,
    ];
}

Multi-locale sites: the package has no i18n concept. Use one column per locale (seo_en, seo_fr) and one field per column, rather than nesting locales inside a single JSON blob — Nova will resolve each column cleanly and you avoid migration pain later.

Hide it from index views. The field renders a whole form of inputs; on an index table it is noise. ->hideFromIndex() is almost always what you want. On Nova 4, wrap it in a Tab::make('SEO') or Panel so the resource form stays readable.

Validate the nested payload. Enforce length limits so editors don't paste 400-character descriptions:

SeoMeta::make('SEO', 'seo')
    ->rules('array')
    ->hideFromIndex(),

If per-key rules aren't supported by the field, add a FormRequest/model mutator that trims the array before save.

Keep image handling explicit. Social platforms need absolute URLs. Store the relative path (Nova's Image-style storage does this), and convert to a full URL at render time as shown above — never emit the raw path into og:image.

Gotchas and Tips

  • Nova version compatibility is the big risk. Last release was September 2020, i.e. the Nova 2/3 (Vue 2) era. Nova 4+ ships Vue 3, and a precompiled field component frequently fails to mount there. Test on your exact Nova version first; if you're on Nova 3 you're likely fine, on Nova 4/5 expect to fork and rebuild the JS or wrap your own fields.
  • Zero dependents, low maintenance. Treat this as vendored code. Fork it into your repo (or a private package) so an abandoned upstream doesn't block a Nova upgrade. The field is small enough to reimplement with a handful of Text/Textarea/File fields plus a JSON column if needed.
  • Null-safety in Blade. A nullable JSON column comes back as null, not []. Always ?? [] before array access, or you'll get "undefined index" on pages that never had SEO filled in.
  • Silent data loss from column mismatch. If the second argument to make() doesn't match the actual column, saves appear to succeed but nothing persists. dd($model->getRawOriginal('seo')) after saving to confirm.
  • JSON vs. discrete columns. Some builds of this package expect separate columns (or a specific JSON shape). Read fillUsing/resolveUsing in the field class before writing your migration — getting this wrong means the field renders but never round-trips.
  • Debugging tip: inspect Nova's /nova-api/... response in the network tab to see exactly what the field emits, and confirm the Vue component name matches what's registered in Nova::script()/service provider. A blank field is nearly always a component-name or asset-registration mismatch, not a database problem.
  • Extension point: the cleanest customisation is extending the field class and swapping the $component, or publishing the compiled assets and patching the Vue component directly. Avoid editing vendor/.
  • Don't overload the description. Trim/strip HTML before displaying, otherwise raw markup leaks into <meta name="description"> — a common cause of "SEO looks broken" bug reports.
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.
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk