Summary
Tag input with pill chips below the field, inline remove buttons, optional static or server-side suggestions, and duplicate-insensitive matching. Extends FilamentTagsInput with FlexTextInput styling, size/variant tokens, and optional Spatie Tags relationship integration via FlexSpatieTagsField.
| Class | Bjanczak\FilamentFlexFields\Filament\Forms\Components\TagsField |
| Spatie class | Bjanczak\FilamentFlexFields\Filament\Forms\Components\Spatie\FlexSpatieTagsField |
| Extends | Filament\Forms\Components\TagsInput |
| State type | list<string> — or string when separator() is set |
| Model cast | 'skills' => 'array' (JSON) or 'labels' => 'string' (comma-separated) |
| FieldType | tags |
| Playground | tags-field slug in Flex Fields playground |
Basic usage
State format
| Value | Description | Example |
|---|---|---|
| Tags (default) | Ordered list of trimmed strings | ['laravel', 'filament'] |
| Comma-separated | When separator(',') is set | 'php,pest,livewire' |
| Empty | [] or null when cleared | [] |
duplicateInsensitive(), comparison is case-insensitive but stored values keep the user’s casing.
Validation
| Behaviour | Detail |
|---|---|
required() | At least one tag must be present |
maxTags() | Caps the number of tags client- and server-side |
suggestionsOnly() | Only values from suggestions (static or server search) are accepted |
duplicateInsensitive() | Treats tags that differ only by case as duplicates |
Inherited TagsInput | Standard Filament rule(), rules(), validationMessages() |
Configuration API — TagsField
Each fluent method accepts aClosure for dynamic configuration.
variant(string|Closure $variant)
Visual style shared with FlexTextInput. Values: primary (default), secondary, flat, soft.
size(string|ControlSize|Closure $size)
Control height. See Control size. Default: md.
maxTags(int|Closure|null $max)
Maximum number of tags. null = unlimited.
suggestionsOnly(bool|Closure $condition = true)
Restrict input to suggestion values only (static suggestions() or server search results).
duplicateInsensitive(bool|Closure $condition = true)
Treat tags that differ only by case as duplicates.
showTagCount(bool|Closure $condition = true)
Show a live tag count below the field (e.g. 2 / 5 tags).
focusOutline(bool|Closure $condition = true)
Inherited from HasFieldFocusOutline. Default: false. When true, shows the shared --fff-field-focus-* ring on the input shell.
Configuration API — inherited TagsInput
TagsField inherits the full Filament TagsInput API. Common options:
| Method | Default | Description |
|---|---|---|
suggestions(array|Closure $suggestions) | [] | Static suggestion list for the dropdown |
splitKeys(array|Closure $keys) | ['Tab'] | Keys that commit the current input as a tag |
separator(string|Closure|null $separator) | null | Dehydrate as delimited string instead of JSON array |
tagPrefix(string|Closure|null $prefix) | null | Display-only prefix on each pill |
tagSuffix(string|Closure|null $suffix) | null | Display-only suffix on each pill |
reorderable(bool|Closure $condition = true) | false | Drag-and-drop tag reordering |
nested(bool|Closure $condition = true) | false | Allow nested tag paths (e.g. foo/bar) |
placeholder(string|Closure|null $placeholder) | — | Input placeholder |
trim(bool|Closure $condition = true) | — | Trim whitespace from tags |
color(string|Closure|null $color) | primary | Filament color token for pills |
disabled(bool|Closure $condition = true) | — | Disable input and tag actions |
readOnly(bool|Closure $condition = true) | — | Read-only display (no new tags) |
Server-side suggestion search
Enable async suggestion lookup withgetSearchResultsUsing(). There is no searchSuggestions() method — server search is configured exclusively through this callback.
When a callback is registered, static suggestions() are not sent to the client; the dropdown fetches results via Livewire instead.
getSearchResultsUsing(?Closure $callback)
Callback signature: function (TagsField $component, string $search): array. Return a list of suggestion strings.
minSearchLength(int|Closure $length)
Minimum characters before the server search fires. Default: 2.
Livewire endpoint
The Alpine component calls the exposed Livewire methodgetTagSearchResults(string $search), which delegates to searchTagSuggestions(). Do not reference getSearchResultsForJs() — that method does not exist on this field.
| Method | Returns | Description |
|---|---|---|
shouldSearchSuggestions() | bool | Whether getSearchResultsUsing() is configured |
getMinSearchLength() | int | Resolved minimum search length |
getSuggestionsForJs() | list<string> | Static suggestions only (empty when server search is on) |
searchTagSuggestions(string $search) | list<string> | Server-side search (PHP) |
getTagSearchResults(string $search) | list<string> | Livewire @Renderless endpoint for Alpine |
Spatie Tags integration (FlexSpatieTagsField)
Use when the Eloquent model implements Spatie\Tags\HasTags. Requires composer require spatie/laravel-tags.
Model setup
dehydrated(false) and persists via Eloquent relationships on save.
| Save path | When |
|---|---|
syncTagsWithType($state, $type) | type('skills') — typed collection |
syncTagsWithType($state, null) | type('') with allowsAnySpatieTagType() === false — untyped tags only |
tags()->sync() via findFromStringOfAnyType | type(null) — any tag type |
type(string|Closure|null $type)
| Value | Behaviour |
|---|---|
'skills' (string) | Load/save only tags of that Spatie type; search filters by type |
null | Allow tags of any type; creates missing tags on save |
Auto suggestion search
FlexSpatieTagsField registers getSearchResultsUsing() automatically — it queries the Spatie tags table (filtered by type() when set). Override with your own callback if needed.
getTagClassName()
Resolves the tag model class from the bound Eloquent model ($model::getTagClassName()) or config('tags.tag_model', Spatie\Tags\Tag::class). Throws RuntimeException if spatie/laravel-tags is not installed.
Edit form — loading existing tags
Relationship hydration is automatic when the form is bound to a record:FlexField schema
Setuse_spatie_tags: true and spatie_tag_type in flex-field schema config to wire through FlexFieldFormBuilder:
| Config key | Maps to |
|---|---|
size | size() |
variant | variant() |
separator | separator() |
split_keys | splitKeys() |
suggestions | suggestions() |
tag_prefix / tag_suffix | tagPrefix() / tagSuffix() |
reorderable | reorderable() |
color | color() |
trim | trim() |
max_tags | maxTags() |
suggestions_only | suggestionsOnly() |
duplicate_insensitive | duplicateInsensitive() |
show_tag_count | showTagCount() |
spatie_tag_type | type() (FlexSpatieTagsField only) |
Model & persistence
JSON array column (default)
Comma-separated string column
Recipes
Taxonomy — suggestions-only with static list
Skills cap — maxTags + duplicateInsensitive + counter
Server search — custom lookup table
Spatie typed tags on a Filament Resource
Flex-field schema — comma-separated labels
Public helper methods
| Method | Returns | Description |
|---|---|---|
getVariant() | string | Resolved variant |
getSize() | string | Resolved size |
getMaxTags() | int|null | Tag cap |
isSuggestionsOnly() | bool | Suggestions-only mode |
isDuplicateInsensitive() | bool | Case-insensitive duplicates |
shouldShowTagCount() | bool | Tag counter visible |
getTagDisplayLabel(string $tag) | string | Tag with prefix/suffix for display |
getWrapperClasses() | array<string, string> | CSS class list for wrapper |
shouldShowFocusOutline() | bool | Focus ring enabled |
CSS classes
| Class | Role |
|---|---|
fff-tags-field | Root field wrapper |
fff-tags-field--{sm|md|lg} | Size modifier |
fff-tags-field--{variant} | Variant modifier (primary, secondary, flat, soft) |
fff-flex-text-input | Shared FlexTextInput shell |
fff-flex-text-input--{size} | Size on input shell |
fff-flex-text-input--{variant} | Variant on input shell |
fi-color-{color} | Filament color token for pills |
has-focus-outline | Focus ring when focusOutline() is enabled |
tag-chips.css (lazy-loaded).
Assets & Livewire
- Uses
wire:ignoreon the Alpine root — state syncs through$entangle. - Lazy-loads
tags-field.css+tag-chips.css+flex-text-input.css. - Alpine entry:
tags-field.jsviax-load. - Server search debounces input and calls
getTagSearchResults()on the Livewire component.
Playground
Slug:tags-field
| Demo field | Shows |
|---|---|
tags_field__basic | Default tags with pills |
tags_field__suggestions | Static autocomplete |
tags_field__comma | separator(',') storage |
tags_field__suffix | tagSuffix('%') display |
tags_field__reorderable | Drag reorder + split keys |
tags_field__max | maxTags(3) + showTagCount() |
tags_field__sm / tags_field__lg | Size variants |
tags_field__secondary / tags_field__soft | Variant tokens |
/admin/flex-fields-playground/tags-field (when playground is enabled).