Forms
A form is a PHP class that declares its fields and handles its own submission. You define the schema once; Lattice renders the React inputs, posts to a dedicated endpoint, validates with Laravel, and runs your handler. Validation runs live as the user types, using the exact same rules.
Defining a form
Section titled “Defining a form”Extend FormDefinition and implement two methods: definition() builds the field schema, and
handle() processes a valid submission. The #[AsForm] attribute gives the form a stable id so it can
be discovered and addressed by its own endpoint.
use Illuminate\Http\Request;use Lattice\Lattice\Attributes\AsForm;use Lattice\Lattice\Forms\Components\Form as FormComponent;use Lattice\Lattice\Forms\Components\TextInput;use Lattice\Lattice\Forms\FormDefinition;use Symfony\Component\HttpFoundation\Response;
#[AsForm('app.profile.form')]class ProfileForm extends FormDefinition{ public function definition(FormComponent $form, Request $request): FormComponent { return $form->schema([ TextInput::make('name', 'Name')->rules(['required', 'string', 'max:255']), TextInput::make('email', 'Email')->email()->rules(['required']), ]); }
public function handle(Request $request): Response { $validated = $this->validate($request);
$request->user()->update($validated);
return redirect('/profile'); }}The schema accepts fields and layout containers (Card, Grid, Stack) in any nesting — see
Fields for the field types and Components for layout.
Rendering a form
Section titled “Rendering a form”Render a form on a page with Form::use(), passing the definition class. Configure it fluently:
use Lattice\Lattice\Core\Enums\HttpMethod;use Lattice\Lattice\Forms\Components\Form;
Form::use(ProfileForm::class) ->method(HttpMethod::Patch) ->submitLabel('Save changes') ->fill([ 'name' => $user->name, 'email' => $user->email, ]);->fill()seeds the fields for an edit form. A field’s filled value wins over its->value()default, and fields can react to it (aSelectresolves stored ids to labels, for example).->method()sets the HTTP verb the form submits with (postby default).->submitLabel()sets the submit button’s text.->context()passes extra data (such as the record id) thathandle()can read back.
The submit lifecycle
Section titled “The submit lifecycle”Every form posts to its own endpoint, resolved from its #[AsForm] id. The request is signed, so a
form only accepts submissions for the schema it actually rendered. FormController routes the request:
- A search request (a searchable
Selectfetching options) returns matching options. - A resolve request (a dependent field recomputing) returns the updated field nodes and values.
- A precognitive request validates and returns
204/422without runninghandle(). - Otherwise the submission is validated and passed to
handle().
$this->validate($request) runs the field rules and returns the validated, cast data as an array —
visible-only, with hidden values and locked (disabled or read-only) user input stripped; a
server-set ->value() on a locked field still comes through. Your handle() returns any Laravel
Response or Responsable: a redirect, a JSON payload, or a toast effect.
Resetting after submit
Section titled “Resetting after submit”Control what the form does with its fields once a submission resolves:
$form ->resetOnSuccess() // clear every field after a successful submit ->resetOnError(['password']); // clear only these fields after a failed submitBoth accept true/false to reset all fields or none, or an array of field names to reset a subset.
resetOnError(['password']) is the common case — clear the password but keep what the user typed
everywhere else.
Working with submitted data
Section titled “Working with submitted data”validate() returns a plain array of validated values. Where a callback needs to read the in-flight
form state — dynamic rules and
dependent fields — it receives a FormData object with typed
accessors:
$data->string('name');$data->boolean('subscribe');$data->integer('quantity');$data->float('price');$data->get('tags', []);Live validation with Precognition
Section titled “Live validation with Precognition”Call ->precognitive() in the definition to validate as the user types, through
Laravel Precognition. The form sends a debounced request
that runs your rules and returns messages without executing handle(). Because it is the same
server-side ruleset, there is nothing to keep in sync.
public function definition(FormComponent $form, Request $request): FormComponent{ return $form ->precognitive(500) // debounce in milliseconds ->schema([/* … */]);}See Validation for the full rule surface.
The submit button
Section titled “The submit button”The form renders its submit button for you, labelled by ->submitLabel(). It is form-aware: it
disables while submitting and while there are validation errors, and shows a spinner — the heading of
that error summary is set with ->validationSummaryLabel() (default “Fix these fields to continue:”).
To take over
placement — render the button somewhere other than the form footer — call ->withoutSubmitButton()
and place a Button with ->submit() in the schema yourself:
use Lattice\Lattice\Ui\Components\Button;use Lattice\Lattice\Ui\Enums\ButtonVariant;
Button::make('Create account')->submit()->variant(ButtonVariant::Default);Next steps
Section titled “Next steps”- Fields — every field type and the options they share.
- Validation — rules, messages, and live validation.
- Conditional fields — show, require, or compute fields from other fields.