alexwenzel/nova-dependency-container
Adds dependency containers to Laravel Nova forms, letting you show or hide groups of fields based on the value of another field. Define conditional logic once and keep complex forms clean, reactive, and free of hand-written JavaScript.
Architecture fit. The package is a presentation-layer primitive for Laravel Nova 4, not a platform component. It introduces two types — DependencyContainer (a group of fields) and DependencyContainerItem (a single-field wrapper) — that declare ->dependsOn($attribute, $valueOrClosure) and render their contents conditionally in the Nova front end. It touches nothing in the data model, the persistence layer, or the resource pipeline: no migrations, no config to publish, no service provider to register manually, no schema change. That is precisely the architectural profile a TPM wants from an admin-tooling dependency — it can be introduced and removed per-resource without cross-cutting refactoring.
Critically, the package's scope is narrower than its name suggests. It provides visual group-level visibility. It does not provide conditional validation, conditional persistence, conditional authorization, or conditional resolve/fill. Hidden fields are still processed by Nova's rule set and still hydrate/save. That boundary is the single most important architectural fact for planning purposes: any requirement that reads as "this field should only be required when X" is not satisfied by this package and must be built as backend validation alongside it. If a stakeholder's mental model of "dependency container" includes business-rule enforcement, correct that in the scoping document before engineering starts — the defect the package most reliably produces is "the form won't save and I can't see why."
Integration feasibility. Low. Installation is a single Composer require with no bootstrap work; the provider registers Nova assets from vendor/.../dist/. The API surface is a handful of lines of documented example plus a small src/ tree that a competent Laravel engineer can read in an afternoon. The realistic integration effort is not the install — it is (a) converting existing conditional-form logic into container declarations, (b) establishing a conditional-validation house rule, and (c) the acceptance-test round trip on records that already hold values in the now-conditional fields. Feasible within a normal sprint by a single engineer, provided that engineer is Nova-fluent.
Technical risk. Three risks matter, in descending order:
'0' vs 0 vs '' vs null) produces a container that never renders — no console error, no log line. This is not a runtime-risk category so much as a review-discipline category; it is neutralised by constants shared between the watching field and dependsOn(), plus one dump of the actual value per field type.Key questions (to close before committing to a date):
dependsOn covers single fields; adopting this package for a one-field case adds supply risk for no capability gain.dist/ present and served in our install path, or do we own a build step?Stack fit. Laravel + Nova 4 + Composer. The package is a leaf dependency with no transitive surface — it does not pull in other libraries and nothing else should be made to depend on its behaviour. Frame it internally as adopt, pin, and treat as vendorable, not staff a small project. It has no place in a Filament or other non-Nova stack, where native dependent fields make it net-negative.
Migration path. Rewrite, do not port. Existing onlyOnForms()/hideFromIndex() toggles and hand-rolled Nova.booting Vue components do not translate mechanically; each conditional becomes a container declaration with its own review. Two authoring conventions should be established on day one and applied to every container thereafter:
protected function premiumBlock(): DependencyContainer) so a container-plus-fields is a named, reusable unit rather than an inline array.Select::make() and dependsOn(). This is the cheapest possible defence against the package's signature defect, a typo'd attribute name that fails open silently.Rule::when(fn () => $request->input('plan') === 'premium', [...], ['nullable']). Hidden is not the same as optional, and this is the team's responsibility, not the package's.Use closure conditions (fn ($value) => in_array($value, [...], true)) for anything richer than equality, and verify the multi-field signature against src/ rather than the README — multi-dependency shape has changed across versions and getting it wrong is silent.
Compatibility. Pin the Nova major in composer.json, pin the package version, and confirm dist/ is present in the tarball before writing code. Debug via the edit endpoint's JSON payload (the dependsOn config ships as field meta) rather than the browser — that isolates "wrong config" from "component not registered" in one step. A field rendering blank rather than absent points at asset registration, not configuration. Also confirm index/detail rendering separately: the container carries no value, but contained fields do not automatically know to stay off the index, and a stray column reads as carelessness.
Sequencing. Three steps, with a hard hold at the end:
Hold at step three until two open items close: the Nova-5 answer and the vendoring decision. Do not expand to a dozen resources while those are unresolved.
Maintenance. Ongoing, not one-time. Budget roughly 4–8 hours per Nova upgrade cycle for revalidation, plus a standing "is it still maintained?" check (release cadence, issue responsiveness, PHP/Nova version support). The package is small enough to fork and MIT makes that cheap, so the maintenance ledger entry is bounded risk rather than unbounded — but it is a real line item and should be recorded as such so it is not re-litigated every planning cycle. Nothing else in the codebase should depend on its behaviour; that containment is what keeps the ledger small.
Support. Effectively self-supporting. There is no SLA, no vendor, and no community backstop at 46 stars and zero dependents. Realistically, the on-call path for a container bug runs through the engineer who adopted it, then through the source, then through a fork. That is acceptable for internal admin screens with a rendering-only blast radius and unacceptable for anything a customer or auditor touches. Decide and document that posture once.
Scaling. Scales linearly with the number of resources and worse than linearly with conditional depth. Adding containers to a dozen resources is fine as long as nesting stays shallow and the validation convention is applied consistently. The real scaling constraint is cognitive: conditional surface area across many resources grows with no natural forcing function, and multi-level conditional forms are hard to reason about, test, and hand off. The written two-level cap is what keeps this linear. If the actual requirement is a multi-step or wizard flow, this package is a poor substitute for real form architecture — recognise that before it becomes scope creep.
Failure modes. Four, ranked by likelihood and cost:
None of these are data-corruption class. That is the central operational argument: the worst realistic outcome is a field rendering wrong on an internal admin screen, which is why the risk profile is tolerable at this dependency quality.
Ramp-up. Shallow for anyone already Nova-fluent: the API is ~10 lines and the source is small. Ramp-up cost is concentrated in conventions, not in the library — the shared-constant rule, the Rule::when validation pattern, and the nesting cap. Those are the artifacts worth writing down, because they are what convert a per-resource judgement call into a checklist. A team without the appetite or capacity to read a small package, pin it, and fork it if needed should treat this as unsupported software and plan accordingly — in which case the honest recommendation is to defer adoption rather than adopt it under a false assumption of support.
How can I help you explore Laravel packages today?