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

Nova Tags Field Laravel Package

marshmallow/nova-tags-field

A Laravel Nova tags field for attaching multiple tags to resources, with a searchable dropdown and tag management in the Nova admin panel. Simplifies many-to-many tagging for models like posts or projects while keeping editing fast and intuitive.

View on GitHub
Deep Wiki
Context7

Getting Started

The package is a single Nova field that maps one database column to a comma-ish free-text tag input. There is no migration, no pivot table, and no model trait — the entire footprint is a field class plus a compiled Vue component.

  1. Install:

    composer require marshmallow/nova-tags-field
    

    (Verify the exact constraint name — the repo is marshmallow-packages/nova-tags-field, and Composer names in small packages often differ from the GitHub slug. Run composer show marshmallow/* after install to confirm.)

  2. Add the column yourself:

    Schema::table('posts', function (Blueprint $table) {
        $table->string('tags')->nullable()->after('body');
    });
    
  3. Register the field on the resource:

    use Marshmallow\NovaTagsField\Tags;
    
    public function fields(NovaRequest $request)
    {
        return [
            Tags::make('Tags')
                ->rules('nullable', 'string', 'max:255'),
        ];
    }
    

Where to look first. Read the field class in vendor/marshmallow/nova-tags-field/src/ — that file tells you the real namespace, the default separator/format, and any fluent options (separator, limit, placeholder). Then check dist/js/ for the compiled component; because the repo is archived you cannot recompile it, so if dist is missing the field simply will not render.

First use case. Labels on a CMS resource — Post, Article, Product — where you want quick visual categorisation in the index view and a free-text input in the form. Anything more relational (tag pages, tag clouds, tag counts) is outside this package's brief.

Implementation Patterns

Normalise at the model boundary, not in Nova. The field stores whatever the user typed. Add an accessor/mutator or a cast so every other consumer of the data sees a clean array:

public function setTagsAttribute($value): void
{
    $tags = collect(explode(',', (string) $value))
        ->map(fn ($t) => trim($t))
        ->filter()
        ->unique()
        ->implode(', ');

    $this->attributes['tags'] = $tags ?: null;
}

public function getTagListAttribute(): array
{
    return array_filter(array_map('trim', explode(',', (string) $this->tags)));
}

Index display. Raw comma strings are noisy in the table view. Either truncate with ->displayUsing(fn ($value) => Str::limit($value, 40)) or use fieldsForIndex / a Badge field driven by tagList to render chips. The latter is usually what stakeholders actually expect to see.

Search. ->searchable() on the field will not work out of the box for a plain column; you need Nova::resource searchable columns or an explicit searchableColumns(['tags' => 'like']). Add a where('tags', 'like', "%{$q}%") to your resource's buildIndexQuery override if you want tag-aware filtering from the search bar.

Filtering. Write a small custom Nova filter rather than fighting the field:

public function apply(Request $request, $query, $value)
{
    return $query->where('tags', 'like', "%{$value}%");
}

For "has any of these tags" filters, explode the input and orWhere each term.

Feed suggestions from live data. If the field supports a datalist/options argument, populate it from DB::table('posts')->whereNotNull('tags')->pluck('tags') and split/flatten. This keeps tag vocabulary from drifting without introducing a real taxonomy.

Keep a graceful exit. Put the field behind a small helper or a resource-level fields() conditional so that when you eventually outgrow it (see below) you can swap to BelongsToMany + spatie/laravel-tags without touching every resource.

Gotchas and Tips

  • Archived, one star, zero dependents. Treat this as a vendored snippet, not a maintained dependency. Pin an exact version in composer.json, and check that the compiled dist assets exist and are compatible with your Nova major — Nova 4/5 moved to Vue 3 and Inertia, and old Nova 2/3 components will silently fail to render.
  • No relational integrity. Renaming a tag means a data migration: UPDATE posts SET tags = REPLACE(tags, 'old', 'new'). There is no join, so "list all distinct tags" is SELECT DISTINCT plus string splitting on the PHP side, which does not scale.
  • Separator collisions. If your content can contain commas, a naive explode(',') corrupts tags. Either pick an exotic delimiter (|) and enforce it in validation with a regex, or store JSON in the column — but then you must also override resolve/fill so the field receives and emits the format it expects.
  • Partial-match false positives. LIKE '%car%' matches cartoon, scar, carousel. Use FIND_IN_SET on normalised comma strings, or accept the fuzziness consciously.
  • Case and duplicate collapse. Default MySQL collations are case-insensitive, so PHP and php become indistinguishable in queries but remain distinct strings in the column. Lowercase on write if you ever group by tag.
  • Null vs empty string. Users clearing the field may leave "" or a trailing comma. The mutator above handles it; without one you get ",,," in the database and blank chips in the UI.
  • Column length. string(255) is about 30–40 tags. If you expect more, use text and drop the max:255 rule, or the input will silently truncate on save.
  • Validation is your only guard. There is no server-side parsing beyond what you add. Use ->rules('nullable', 'string', 'max:255', 'regex:/^[a-z0-9 ,\-]*$/i') to keep the delimiter clean.
  • Extension points. Subclass the field to override resolve() / fillUsing(), and pass ->component('your-custom-tags') if you need to fork the Vue side. Since you cannot rebuild the archived assets, a fork means writing your own component anyway — at which point evaluate whether the package earns its place at all.
  • Upgrade trigger. The moment you need tag counts, tag pages, tag renaming across records, or multi-resource tag sharing, migrate to a proper tagging package with a pivot table. This field is a stopgap for a single resource's labels, and the migration cost grows with every row you write.
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.
snowsoft/faker
meditationsteps/blogetc
animalyoung/laravel-influxdb
onix-solutions/sms-telera
lonely-lights/prosetta
oxygen/data
oxygen/core
jengo/base
xiloryx/log-collector
a21ns1g4ts/filament-stripe
la-php/laravel-admin
artisan-build/community-prompts
vleroy/smart
ayles-software/laravel-australian-public-holidays
cboxdk/laravel-id-spatie
cboxdk/laravel-id-billing
cboxdk/laravel-id-billing-bridge
simple-as-fuck/php-performance-log
hsluv/hsluv
seampact/codegen