rhyslees/nova-tags-improved
An improved tags field for Laravel Nova that provides a smoother tagging experience with searchable options, inline tag creation, and configurable behavior for managing tag relationships in Nova resources.
rhyslees/nova-tags-improved is a tags field for Laravel Nova — a fork that extends the common "tags input" pattern (type-to-create, chip UI, optional grouping) inside Nova resources. With only 2 stars, no dependents, and a last release in February 2023, treat it as a vendor-inlined utility rather than infrastructure you trust blindly. Pin the version.
Install
composer require rhyslees/nova-tags-improved
php artisan vendor:publish --tag=nova-tags-improved-config # if the tag exists
If no config tag exists, check vendor/rhyslees/nova-tags-improved/ for config/, resources/, and src/ — the fork's README on GitHub is the authoritative source; read it before assuming any API.
Register the field
use RhysLees\NovaTagsImproved\Tags;
public function fields(Request $request)
{
return [
ID::make()->sortable(),
Text::make('Title'),
Tags::make('Tags'),
];
}
First use case: an Article model with a tags column (JSON/array cast) or a spatie/laravel-tags relationship. Open the resource's fields() method, drop the field in, and verify the storage strategy the package expects before migrating — see Gotchas.
Where to look first, in order:
spatie/nova-tags-field). The "improved" changes are the only reason to pick this over upstream.composer.json — confirms Nova version constraint and whether it depends on spatie/laravel-tags.src/ field class — shows the resolve*/fill* methods, which dictate your column type.Pattern 1 — JSON column, no extra package. The simplest and safest for a low-activity fork:
// migration
$table->json('tags')->nullable();
// model
protected $casts = ['tags' => 'array'];
Tags::make('Tags')
->help('Comma separated, press Enter to add');
The field writes an array into the column; display via a badge component or Text::make('Tags', fn ($a) => implode(', ', $a->tags ?? [])).
Pattern 2 — spatie/laravel-tags relationship. If the fork inherits the Spatie field's API:
Tags::make('Tags')
->type('article') // Spatie tag type
->withLinkToTagResource() // navigation to Tag resource
->onlyOnForms();
Guard this pattern carefully — it only works if the fork kept the Spatie integration intact. Confirm by grepping the src/ for Spatie\Tags imports.
Pattern 3 — Grouped / scoped tags. Common "improvement" over upstream is a ->group() or ->type() modifier so tags for articles don't pollute products. Wire it into the same JSON pattern with a nested structure:
Tags::make('Tags')->group('article');
// stores ['article' => ['laravel', 'nova']]
Pattern 4 — Index/detail display. Tags fields are typically form-only. If you want chips in the index, resolve them explicitly:
Text::make('Tags', fn ($a) => collect($a->tags ?? [])->take(3)->implode(' · '))
->onlyOnIndex(),
Integration tips
whereJsonContains('tags', $value)) or the Spatie pivot, not a LIKE on a serialized string.->searchable(), back it with the same whereJsonContains logic in the model's searchable() scope; otherwise skip it.src/ yourself. Prefer it only when its "improvements" are concretely useful to you — otherwise reach for spatie/nova-tags-field directly.composer.json replace/require, and the README header). Copy the upstream's docs into your notes; the fork's README may be stale or incomplete.fill() / resolve() on the field class. A JSON cast, a plain string, and a Spatie relation are three different migrations — picking the wrong one produces silent null on save or a PHP array to string exception on retrieve.composer.json will tell you the laravel/nova constraint. A 2023 release almost certainly predates Nova 5. If you're on Nova 5, test in a branch before merging — field component registration changed between Nova 4 and 5.Tag model. If you care about who can create tags, wrap the field in ->canSee(fn () => $user->can('create', Tag::class)) or lock the field to ->readonly() for non-admins.Resource::indexQuery(...)->with('tags'). This is invisible in tests with 3 rows and obvious at 300.vendor:publish finds nothing under the package's tag, check the service provider's boot() for a differently named tag. A silent no-op publish is a common foot-gun with low-maintenance forks.$request->input('tags') in fill() first, then the model's $attributes['tags']. The gap between those two is almost always a $fillable/$casts mismatch, not a package bug.Tags in app/Nova/Fields/ and override resolve()/fill() rather than editing vendor/ — this also gives you a clean migration path off the package later.How can I help you explore Laravel packages today?