Skip to main content
Firstlight

Firstlight / Docs

Recipes for the screens a forms app needs, setup for your coding agent, Laravel on native screens, and accessibility.

View source
Submit Firstlight forms

Submit Firstlight Forms

Firstlight submits forms through an ordinary Button press and a PHP screen method. There is no <firstlight:form> container, client-side validator, or form schema.

Declare the screen

Extend FirstlightUI\NativeComponent to use the form submission and field validation helpers. Declare Laravel rules against the public properties bound by your fields:

<?php

namespace App\Native;

use App\Models\Profile;
use FirstlightUI\NativeComponent;

class EditProfileScreen extends NativeComponent
{
    public string $name = '';

    public string $email = '';

    protected array $rules = [
        'name' => 'required',
        'email' => 'required|email',
    ];

    public function save(): void
    {
        $this->submit(
            action: fn () => Profile::query()->updateOrCreate(
                ['user_id' => auth()->id()],
                ['name' => $this->name, 'email' => $this->email],
            ),
            successMessage: 'Profile saved',
        );
    }
}

Screens that extend Native\Mobile\Edge\NativeComponent directly can opt in with both FirstlightUI\Concerns\ValidatesFields and FirstlightUI\Concerns\SubmitsForms.

Bind the fields and Button

Use the existing fields, native:model, and Button:

<firstlight:text-field
    label="Name"
    native:model.blur="name"
/>

<firstlight:text-field
    label="Email"
    native:model.blur="email"
/>

<firstlight:button @press="save">
    Save profile
</firstlight:button>

submit() runs $this->validate() first. A validation failure returns false, skips the callable and success Feedback, and lets ValidatesFields publish each first Laravel message through the matching field's native error slot. Screen readers hear a summary and move to the first invalid field; add <firstlight:callout error-summary /> for a visible summary. See Validate Firstlight fields for rule sources, Form Requests, custom messages, and explicit error binding. The ValidatesFields reference lists the method contract and participating fields.

After successful validation, submit() runs the callable and sends Feedback::success($successMessage)->send() when the message is non-empty. It then returns true. Pass validate: false only when the action does not need field validation:

$sent = $this->submit(
    action: fn () => $this->sendVerificationEmail(),
    successMessage: 'Verification email sent',
    validate: false,
);

To fail the whole form from inside the action, throw Laravel's ValidationException. Its messages replace the screen's error bag, submit() returns false, and no success Feedback is sent:

use Illuminate\Validation\ValidationException;

$this->submit(
    action: function (): void {
        if (! Auth::attempt(['email' => $this->email, 'password' => $this->password])) {
            throw ValidationException::withMessages([
                'form' => 'These credentials do not match our records.',
            ]);
        }
    },
    successMessage: 'Signed in',
);
<firstlight:callout error-for="form" />

Other unexpected exceptions from the callable are not converted to Feedback or swallowed.

Loading and duplicate submissions

$submitting is true while validation and the callable run. Re-entering submit() on the same PHP component during that interval returns false without validating, calling the action, or sending Feedback. submit() always clears $submitting in finally, so binding Button loading or disabled to $this->submitting does not flash during the default one-request path.

NativePHP handles a press and then compiles one Element Tree afterward. A device-visible loading frame requires application code to set $this->submitting = true outside submit(), publish that tree, and clear the flag on a later request.

The helper does not publish an intermediate tree, call publishPlaceholder(), or add a client-side loading mechanism. publishPlaceholder() belongs to lazy screen mounting, not form submission.

Served from firstlightui/nativephp on GitHub.