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

Laravel Nova Field Dynamic Select Laravel Package

hubertnnn/laravel-nova-field-dynamic-select

A Laravel Nova field for dynamic select inputs, letting dropdown options update based on other form fields so dependent selects stay accurate without custom JavaScript. Useful for cascading choices, filtered options, and linked form fields in Nova resources.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and confirm the wiring

composer require hubertnnn/laravel-nova-field-dynamic-select

The package ships a field class plus compiled JS/CSS assets. Auto-discovery should register its service provider; if the field renders without behaviour (no options refresh when the parent changes), check config/app.php for the provider and run php artisan nova:publish / vendor:publish so assets land under public/vendor/.

Where to look first

  1. README.md — the authoritative example for this package's exact method names (dependsOn, the options closure/array, any optionsFrom/using variants). Small packages rename things between minor versions; trust the README over blog posts.
  2. src/ — the field class (usually DynamicSelect extending Nova's Select). Read options(), dependsOn(), and resolve() to see exactly what gets sent to the browser and when the closure runs.
  3. resources/js/field.js — the front end. It tells you which request the field makes, what payload it posts (parent values), and whether responses are debounced.

First use case: a cascading select

The canonical scenario is Country → City on a resource form: a normal Select for the parent, a DynamicSelect for the child whose option list is recomputed whenever the parent changes.

use Laravel\Nova\Fields\Select;
use Hubertnnn\LaravelNovaFieldDynamicSelect\DynamicSelect;

public function fields(Request $request)
{
    return [
        Select::make('Country', 'country_id')
            ->options(Country::pluck('name', 'id')),

        DynamicSelect::make('City', 'city_id')
            ->dependsOn('country_id')
            ->options(fn ($countryId) => City::query()
                ->where('country_id', $countryId)
                ->orderBy('name')
                ->pluck('name', 'id')
                ->all()),
    ];
}

Smoke test: open the create form, change the country, and watch the network tab — you should see a request carrying the parent value and a fresh option list in the response. Then edit an existing record and confirm the stored city_id still preselects.

Implementation Patterns

Multiple parents. Most implementations accept an array; the closure then receives one argument per parent, in order.

DynamicSelect::make('Locality', 'locality_id')
    ->dependsOn(['country_id', 'region_id'])
    ->options(fn ($countryId, $regionId) => Locality::query()
        ->when($countryId, fn ($q) => $q->where('country_id', $countryId))
        ->when($regionId, fn ($q) => $q->where('region_id', $regionId))
        ->pluck('name', 'id')
        ->all()),

Multi-level chains. Country → Region → City works by chaining: Region depends on country_id, City depends on region_id. Keep the fields in dependency order in fields(); the JS wiring usually binds by field attribute name at render time.

One resolver, three consumers. Extract the query into a method or small class so the field options, your validation rule, and any displayUsing all read from the same source. This is the single biggest maintainability win with dynamic selects:

private function cityOptions(?int $countryId): array
{
    return $countryId
        ? City::where('country_id', $countryId)->orderBy('name')->pluck('name', 'id')->all()
        : [];
}

Validation stays yours. The field only supplies options; it does not validate. Pair it with Rule::in(array_keys($this->cityOptions($request->country_id))) or an exists:cities,id rule, otherwise a crafted request can submit any ID.

Edit forms and display. On the form the stored value preselects because Nova hydrates the field's value before the JS runs — but the option list for that value must be derivable from the parent, so the parent field must also be present and populated on the edit form. On index/detail, the options closure is not executed; you get the raw key unless you add displayUsingLabels() (if the field inherits it from Select), a displayUsing() callback, or model the relationship with BelongsTo for display purposes.

Reuse across resources. Because the field is just a class, wrap your common cascades in a small factory (CitySelect::make('city_id')) returning a configured DynamicSelect. That keeps form definitions readable when several resources share the same geography or taxonomy.

Testing. Render the form definition with Nova's assertFields helpers to catch wiring mistakes (wrong attribute name in dependsOn), and unit-test the resolver method directly — that's where the real logic lives. If the package exposes an endpoint for the AJAX refresh, feature-test it with an authenticated Nova user.

Gotchas and Tips

  • dependsOn takes the attribute, not the label. With Select::make('Country', 'country_id'), depend on country_id. This is the most common silent failure — the field simply never refreshes.
  • Empty parent. Decide explicitly what an empty parent returns. Returning all rows produces a huge, misleading dropdown; return [] and combine with ->nullable() plus a placeholder so the field is usable while the parent is unset.
  • The closure runs on form serialization, not per keystroke. Understand where it fires: for the initial render and for each dependent-refresh request. If it's expensive, cache it — a Cache::remember keyed on the parent value (and tenant) is usually enough.
  • N+1 and latency. Each dependent select can trigger an HTTP round-trip. Forms with several chained selects multiply this. Keep option queries indexed, return only id => label, and avoid high-cardinality option sets without search or pagination. Watch p95 latency in the network tab before shipping.
  • Authorization on the options source. The package does not know about your policy layer. If you fetch options from a tenant-scoped or policy-guarded query, scope it explicitly inside the closure — a dynamic select can otherwise leak record names across tenants.
  • Version sensitivity. Nova fields are Vue components coupled to Nova's field lifecycle. Pin the exact version in composer.json, smoke-test rendering/refresh/validation/save on every Nova upgrade, and be ready to fork or replace it (22 stars, zero dependents, single-maintainer risk). Keep it behind your own factory so swapping to a hand-rolled field is a one-file change.
  • Silent failure mode. If the refresh endpoint errors, you typically get empty or stale options with no visible message. Add logging/monitoring on the endpoint and consider a graceful "could not load options" state.
  • Precedence and defaults. Setting a default value on a dynamic select only makes sense when the parent is already populated; otherwise Nova hydrates a value with no matching option. Seed parent defaults first, or use ->default() only on top-level selects.
  • Asset cache. After editing the package's JS or bumping versions, php artisan nova:publish plus a hard refresh — Nova's compiled assets are aggressively cached and a stale public/vendor bundle looks exactly like a broken dependsOn.
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.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource