erag/inertia-forms
erag/inertia-forms simplifies form handling in Laravel Inertia apps, pairing server-side validation with a lightweight frontend API for submitting, resetting, and displaying errors—cutting boilerplate and keeping forms consistent.
Architecture fit. The package is a server-driven form-definition layer on top of Inertia.js. It is philosophically consistent with Inertia's contract — controllers return props, the frontend renders them — but it pushes far more of the form into PHP: the field tree, layout (fieldsets, columns(), columnSpan()), validation rules, conditional visibility, submit target, and submit intent. For a team already on Laravel + Inertia, that is a natural extension rather than a new paradigm. For a team on Livewire, Filament, Blade-only, or a headless API with a separate SPA, it is a poor fit: it assumes Inertia page props plus a framework-specific JS adapter.
The core value proposition is real: one PHP class is the single source of truth for structure, validation, and flow, rendered by one <Form> component. That eliminates the classic drift between frontend field definitions and backend FormRequest rules. The #[Validate] attribute on controller actions is the piece that closes the loop.
Key architectural constraint. The form definition is a PHP object serialized into props, so the frontend must know how to render every component name in the tree. Custom fields require coordinated PHP + frontend work and registration via <Form :components="{ Name: Component }" />. That is a contract you end up owning. Also note the surface is effectively four packages: the PHP package plus -vue, -react, -svelte adapters.
Integration feasibility. Feasible for a greenfield Laravel 11/12 + Inertia app on any of the three adapters. The documented workflow is coherent end-to-end: make:form → fields() → controller returns Form::make()->bind($model) → <Form :form="form" /> → #[Validate] FooForm $form in the update action → $form->validated(). The main friction points are the opinionated routing (the form must know its actionRoute server-side) and the PHP↔JS version pairing.
Technical risk.
visibleWhen() is cosmetic; authorizedWhen() is the actual authorization control. Any developer who confuses them creates an authorization gap. The Html field renders raw markup — a potential XSS surface if fed user content. validated() excluding hidden/unauthorized fields is a package guarantee that should be verified in tests, not assumed.app/Http/Requests into form classes reduces grep-ability and breaks the conventional mental model for new joiners.Key questions to answer before committing.
#[Validate] interact with Rule::unique, Rule::exists, after() hooks, prepareForValidation, policies, and custom rule objects?aria-invalid, error announcement, wizard focus management?validated() be unit-tested without booting Inertia?searchUsing()/selectedOptionsUsing())?Stack fit. Target Laravel 11/12, Inertia (pin the major the adapters support), and exactly one of Vue 3 / React 18+ / Svelte 4+. Prerequisite: the app is already Inertia-driven with components resolving from resources/js. Pick one adapter and commit to it for the pilot; if you are on React or Svelte, run a parity spike first rather than assuming the Vue-documented behavior transfers.
Migration path. Two-track:
FormRequest forms. Convert opportunistically when a form is being substantially redesigned anyway. Keep FormRequest for anything policy-heavy or where the same validation logic is shared with APIs, importers, or queued jobs.Compatibility. Pin the PHP package and the matching JS adapter to exact versions in composer.json/package.json — no caret ranges given a single release. Lock the Inertia major. Record the PHP↔JS adapter compatibility matrix in the repo, and configure Renovate/Dependabot to group the erag packages so they always upgrade together.
Sequencing (roughly 6–8 weeks for a pilot).
authorizedWhen case, one file upload, one server-search combobox. Measure render latency and bundle-size delta against a hand-written Inertia page.authorizedWhen vs visibleWhen, where form classes live (app/Forms), naming, intent handling, and whether wizards are permitted.validated() excludes hidden and unauthorized fields.Html is never reachable with untrusted input.Fork contingency. Because the JS adapter is the thin, replaceable layer and the PHP DSL is the durable asset, keep form definitions free of adapter-specific quirks so a swap or fork is mechanical. Maintain a fork remote so you can patch.
Maintenance. Cost is dominated by two things: upgrading a four-package version set in lockstep, and owning the custom-field surface you build. Each custom field is a PHP class plus a frontend component plus registration plus accessibility plus tests — budget roughly half a sprint for the first, one to two days thereafter. Because validation now lives in form classes, "where is this validated?" questions shift; document that loudly in CONTRIBUTING and the team wiki.
Support. With 24 stars and 0 dependents there is effectively no community to lean on. Assume maintainer response times of days-to-never and that you will be reading source rather than finding Stack Overflow answers. Mitigations: assign one named owner (not a committee), keep a vendor-risk note in the architecture decision record, and keep a local fork remote. The llms.txt docs index is a genuine plus for AI-assisted development, but it is not a substitute for a test suite you control.
Scaling. Two axes:
app/Forms is greppable and consistent. Watch prop payload size on large repeater/blocks forms, and watch for N+1 queries when populating Combobox::options() from collections. Use server-side search beyond a few hundred options.Failure modes to plan for.
visibleWhen used where authorizedWhen was required → PR checklist item plus a grep/static rule in CI.Html field with user-controlled content → restrict to trusted templates, treat as an XSS surface.validated() from a package bug → assert with a targeted test rather than trusting the guarantee.Ramp-up. A mid-level Laravel developer with Inertia experience should be productive on basic fields within a day, and on wizards, conditional logic, and intents within a week — the DSL is fluent and the field table is comprehensive. The longer ramp is judgment: when to use a package form versus a hand-written Inertia page, and internalizing the visibility-vs-authorization distinction. Plan a one-hour internal walkthrough and a short cookbook. Frontend developers only need the component registry if custom fields are on the roadmap.
Bottom line for a TPM. The value proposition — one PHP source of truth for form structure, validation, and flow, rendered by one component — is compelling and well-matched to an Inertia codebase. But at 24 stars, one release, zero dependents, and a four-package surface, this is a pilot-stage dependency, not a platform bet. Recommend a time-boxed spike on two or three real forms, exact version pins, a named owner, and the fork option held open. Standardize only after the pilot has survived at least one upgrade cycle and a formal accessibility review.
How can I help you explore Laravel packages today?