nyoncode/wire-forms
A Laravel Livewire package for building and managing forms with reusable components, validation, and a clean, expressive API for handling fields, submission, and errors in your Livewire applications.
Architecture fit. wire-forms targets a well-defined seam in a Laravel/Livewire codebase: the form layer that every product rebuilds — field definitions, state binding, validation wiring, error rendering, layout grid, repeaters, reset semantics. Architecturally it is a thin presentation abstraction, not a framework, not a panel. That is the right shape for the only adoption posture this package supports: internal tools, back-office CRUD, settings screens, MVPs. It is the wrong shape for anything where form behaviour is a product differentiator, where accessibility or i18n is contractual, or where Filament/Nova already owns the admin surface. A TPM's first job is scoping adoption to the former and writing the latter down as an explicit non-goal so it is not relitigated per-sprint.
The strategic question to bank or discard early is the "standalone" claim. Two readings have very different architectural consequences:
| Reading of "standalone" | Consequence | PM posture |
|---|---|---|
| One schema drives both Livewire components and controller-validated forms | Genuine decoupling — panel-grade form behaviour without admin-panel lock-in | Worth a spike; this is the reason to accept the risk |
| "Usable without the parent NyonCode suite" | Thinner Filament clone; loses the strategic reason to adopt | Decline or downgrade to a pure velocity play |
Do not let the team build an architecture story on the first reading until someone has confirmed it in source. This is a gate, not an assumption.
Integration feasibility. Feasible, and cheap to test — the package is small enough to read in an afternoon and MIT-licensed, so the code can be forked, vendored, and patched. Integration is bounded: a form class per resource, instantiated in a Livewire component, bound in Blade, validated on submit. The real feasibility risk is not "can we wire it up," it is "does it behave correctly at the edges we care about." Three feasibility questions dominate, in order:
Illuminate\Validation\Factory? Look for Validator::make, Rule contract handling, and whether FormRequest and custom Rule objects survive intact. Any evidence of hand-rolled rule evaluation ends the evaluation. Every divergence becomes an exploitable defect, and at zero dependents there is no upstream security process to report into. This is the single go/no-go gate and it must be owned by a named engineer, not "the team."livewire/livewire alongside the package and gate both behind a single dependency PR.aria-*, wire:model modifiers, and data-* test hooks disappear without anyone noticing. This is the accessibility failure mode and it needs an explicit test on day one.Technical risk. The dominant risk is not code quality — it is bus factor of one combined with zero public evidence of use. Zero stars, zero dependents, no issue history, no downstream corpus, no composite score. Assume the maintainer goes quiet the week after adoption. Everything else is secondary:
^0.x permits breaking changes in a patch. Pin the exact version (0.1.4). Non-negotiable.blade-ui-kit and existing Livewire component registrations — low impact, cheap to check early.$errors elsewhere; inline messages silently fail to appear. Cheap to catch in a spike, expensive to catch in QA.Key questions the evaluation must answer before a go decision.
Illuminate\Validation\Factory and pass Rule/FormRequest objects through unchanged? (blocking)composer.json constraints on php, laravel/framework, and livewire/livewire cover our current stack and the next Laravel LTS? (blocking)items.*.name) the way Laravel expects?reset() / re-mount path clean between records?Stack fit. Requires Laravel 10/11+ with Blade and Livewire 3. Fit is good where form work is repetitive rather than novel and the field vocabulary is conventional — text, select, standard validation rules, a layout grid. Poor fit for edge-case-heavy forms (complex conditional logic, heavy file handling), API-first Inertia/Vue front ends where server-rendered field components add little, and any environment already running Filament, where you would be maintaining two layout systems and two validation paths for one job.
Migration path. There is no migration path, and that is deliberate. New forms only. Do not migrate existing screens. Adoption risk must be bounded to code the team was going to write anyway; retrofitting an unproven abstraction onto working screens converts an experiment into a migration project with no offsetting benefit. The corollary is a scope commitment the TPM should state plainly in the kickoff: existing form code is frozen with respect to this package, and any request to migrate it routes through a separate decision with its own risk case.
Compatibility. Three compatibility surfaces need explicit controls:
0.1.4), never a caret on 0.x. Livewire pinned in lockstep. Both upgrades routed through one dependency PR with the fixture form as smoke test.composer why-not nyoncode/wire-forms 0.1.4 against the lockfile before any code is written; confirm coverage of current and next Laravel LTS.blade-ui-kit, existing Livewire component registrations, and any shipped views/CSS/JS. Version or namespace package assets so they don't fight the design system on the next upgrade.Sequencing. A timeboxed, gated sequence — each step produces an artefact and has a defined stop condition:
composer.json constraints. Stop condition: hand-rolled rule evaluation → evaluation ends here.wire-forms. Roughly a day. Payoff: swapping to filament/forms, native Livewire form objects, or raw Blade later becomes a mechanical edit rather than a rewrite. Without the seam, "cheap to try" stops being true at forty forms.required|email, one conditional field, one submit. Not the checkout page, not anything customer-facing. Confirm: errors land in the bag Blade already reads; failed submit repopulates without manual fill(); unknown props fail loudly.Validation layering is an architectural constraint, not a recommendation. Write it into the ADR: the package's validation is a UX affordance — fast feedback, inline messages, no full-page round trip — and is never the trust boundary. A FormRequest or explicit authoritative $this->validate() stays on the server-side write path for anything touching money, permissions, or persisted records. If form-object rules and FormRequest rules drift, we have two sources of truth and the weaker one is public-facing.
Maintenance. Budget honestly: 5–10% of one engineer's time in year one for fork upkeep, dependency bumps, and internal docs. If upstream activity materialises, that drops. If it stays dormant, it rises — which is precisely what the fork and the adapter seam exist to absorb. Assign a named owner. An unowned fork is a fork that rots, and the failure will surface as a blocked Livewire upgrade six months out with nobody holding context.
Support. There is no upstream support channel worth planning against: no issue history, no downstream corpus, no community. Internal support means internal docs, internal tests, and internal ownership. Two artefacts carry most of that weight and should be treated as deliverables, not nice-to-haves:
Scaling. Two distinct scaling axes with opposite answers:
aria-describedby consistently. Retrofitting aria-invalid across forty screens later is a genuinely miserable sprint, and it is the kind of work no roadmap ever accounts for.Failure modes. Named, with the detection and the response:
| Failure mode | Signal | Planned response |
|---|---|---|
| Upstream goes dormant | No commits/releases over two quarters | Already forked; absorb through the fork. Expected case, not an incident |
| Livewire major upgrade breaks the package | Fixture form fails on the dependency PR | Patch via fork; if the patch is more than a day, escalate the exit decision |
| Validation delegation turns out to be hand-rolled | Spike step 2 | Evaluation ends. Pre-committed, not a debate |
| Silent attribute dropping | Accessibility test on unknown prop | Wrap the field component so it forwards; do not accept silent loss |
| Error bag mismatch | Inline messages don't render while validator logs a failure | Debug order: hydration → rule string reaching Validator::make → bag identity |
| Stale state across records | Create form pre-filled with last edited record | Test reset()/re-mount explicitly in the spike |
| Rules empty on second request | Validation "doesn't fire" | Hydration problem, not a validation problem — dd($this->form->rules()) in the submit handler |
The one failure mode that is genuinely unmitigable is silent validator divergence in production, because there is no upstream security process to disclose into. That is why the gate at step 2 is a hard stop rather than a risk to be accepted with monitoring.
Ramp-up. Low for the mechanics, higher than it looks for the judgement. A Laravel developer reads the source in an afternoon and lands the first form in a day. The non-obvious skills are the ones that decide whether adoption pays off: keeping the form class a thin schema description and keeping authorization, persistence, and side effects out of it (form objects that grow a save() method become the thing you cannot test); testing the form class rather than the markup; and resisting the urge to migrate existing screens. Plan for a half-day of internal walkthrough on the adapter seam and the fixture form, and write the validation-layering constraint into the ADR so it survives team turnover.
Go/no-go framing for stakeholders. The decision is reversible if the seam exists and the fork is owned. Downside is bounded to integration effort, not a migration project. Where it fits: internal tools, back-office CRUD, settings screens, MVPs, prototype-heavy phases. Where it doesn't: customer-facing differentiator forms, accessibility- or i18n-contractual surfaces, payment-adjacent flows, or a roadmap that depends on this layer for years rather than months.
How can I help you explore Laravel packages today?