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 Multiselect Field Laravel Package

optimistdigital/nova-multiselect-field

A searchable multiselect field for Laravel Nova with tag support, relationship-friendly options, and a clean Vue-powered UI. Add multi-value selects to resources and forms while keeping Nova’s native look and feel, with flexible configuration for options and display.

View on GitHub
Deep Wiki
Context7

Getting Started

Install the package with Composer (note that the Composer name and PHP namespace differ — see Gotchas):

composer require optimistdigital/nova-multiselect-field

Nova 4/5 auto-registers the field's assets through the package's service provider; no vendor:publish or manual Nova::script() call is normally needed. Just make sure you're on the major that matches your Nova version (^4.x for Nova 4, older tags for Nova 3).

First use case — a tags column on a Post model:

use Outl1ne\MultiselectField\Multiselect;

public function fields(NovaRequest $request)
{
    return [
        Multiselect::make('Tags', 'tags')
            ->options([
                'laravel'  => 'Laravel',
                'vue'      => 'Vue',
                'nova'     => 'Nova',
            ])
            ->saveAsJSON()
            ->searchable()
            ->rules('array'),
    ];
}

Add the cast on the model when using saveAsJSON():

protected $casts = ['tags' => 'array'];

Two things to decide immediately, because they're hard to change later: storage format (comma-separated string is the default; saveAsJSON() is almost always what you want) and whether the column is text/json. If in doubt, use JSON — it survives values containing commas, and it round-trips cleanly against the array cast.

Implementation Patterns

Static options. The simplest form is a key => label array. Keys become the stored values, labels are what the user sees. Stick to scalars — strings and ints are safe; objects and arrays are not.

Dynamic / dependent options. options() accepts a closure, which is evaluated server-side each time the field resolves:

Multiselect::make('Subcategories')
    ->options(fn () => Category::where('parent_id', $this->parent_id)->pluck('name', 'id'))
    ->saveAsJSON()
    ->dependsOn('parent_id'), // Nova reloads the field when parent_id changes

This is the idiomatic pattern for cascading selects. Keep the closure cheap — it runs on every resolve, including index rows if the field is visible there.

Large lists. Add ->searchable() (backed by vue-multiselect) and ->max(n) to cap selections. ->placeholder('Pick tags…') is worth setting for discoverability, especially on empty nullable fields.

Validation. The request payload arrives as an array, so pair it with ->rules('array') and validate the values, not the labels:

->rules('array')
->rules(fn () => ['array', Rule::in(array_keys($this->allOptions))])

->nullable() plus array rules avoids "must be an array" errors on optional fields.

Display. On index/detail views the raw stored value is shown unless you customize it. Use Nova's displayUsing() to map keys to labels for a readable index column, and hideFromIndex() if the column is noisy. Because the stored value is often IDs, a naive index shows 3,17,42 — worth fixing early.

Multiple fields, one resource. Fields are independent; there is no shared state. If you need pivot-backed many-to-many, use Nova's BelongsToMany/MorphToMany instead — this field is for a single denormalized column, not relations.

Gotchas and Tips

  • Vendor/namespace mismatch. Composer package is optimistdigital/nova-multiselect-field, the PHP namespace is Outl1ne\MultiselectField, and the GitHub org is outl1ne. Don't "correct" the import to OptimistDigital\... — it will fail.
  • Default storage is a comma-separated string. Any value containing the separator corrupts the data silently. Use saveAsJSON() unless you have a strong reason not to. If you must use strings, saveAsString() accepts an alternate separator in recent versions — verify the signature for your pinned release.
  • Don't double-encode. With saveAsJSON() + the array cast, the package handles encoding. If you also json_encode in a mutator or accessor, you'll get "[\"a\",\"b\"]" stored as a string and a broken field on edit. Pick one layer.
  • Options callable shape. Return pluck('label', 'id') or an associative array. Returning a list of Eloquent models produces labels like {"id":1,...} and a confusing UI.
  • Eager-load / cache options. A closure hitting the DB for every index row is a classic N+1. Prefer ->onlyOnForms() or hideFromIndex(), or memoize the query result.
  • Empty submissions. A cleared field can post [], null, or be absent depending on Nova version. If you see stale values on save, confirm your fillAttributeFromRequest path is receiving the key — log $request->all() in a temporary resolve/fill override.
  • Debugging field meta. dd($field->json, $field->options) in a throwaway route, or inspect the fields payload in the Nova resource response in the browser network tab. Most "options not showing" issues are shape or visibility problems, not JS.
  • Version pinning. Because the package is renamed and actively tagged, pin a major in composer.json and check the changelog when bumping Nova — the asset build (Nova 3 webpack vs Nova 4 Vite) changed across majors and a mismatch results in a field that renders as an empty div.
  • Extending. Custom behavior usually belongs in resolveUsing() / displayUsing() or a subclass overriding options(), rather than patching vendor files. The package ships no views worth publishing for typical use.
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.
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle