Form Class
Defining Fields
Every form class must extend Form and implement the fields() method. Return an array of Field instances:
use InertiaUI\Forms\Fields\TextInput;
use InertiaUI\Forms\Fields\Textarea;
use InertiaUI\Forms\Fields\Submit;
use InertiaUI\Forms\Form;
class FeedbackForm extends Form
{
public function fields(): array
{
return [
TextInput::make('name')->required(),
TextInput::make('email')->email()->required(),
Textarea::make('message')->rows(5)->required(),
Submit::make('Send Feedback'),
];
}
}Labels are auto-generated from the field name. first_name becomes "First Name", email stays "Email". You may always set a custom label with ->label('Your Email Address').
Text and HTML Rendering
Field labels, help text, fieldset descriptions, and placeholders are plain text. Frontend renderers escape text content, and placeholders render as HTML attributes.
Do not pass HTML to label(), help(), fieldset description(), or placeholder(). Use the RichText field when users should edit and submit HTML, display helpers for generated form copy, or a custom wrapper/slot for richer surrounding content.
Creating Instances
Use the static make() method to create a form instance:
$form = FeedbackForm::make();This resolves the form through Laravel's service container, so you may type-hint dependencies in the constructor if needed.
Action URL
Route Name
Set the action using a named route, either as a property or fluently:
// As a property
class CreateUserForm extends Form
{
protected ?string $actionRoute = 'users.store';
}
// Fluently with parameters
$form = EditUserForm::make()->route('users.update', ['user' => $user]);Raw URL
You may pass a raw URL instead of a route name:
$form = MyForm::make()->url('/api/submit');HTTP Method
The form defaults to POST. Named routes automatically detect the method from the route definition, and that method takes precedence over an explicitly configured method. Set the method explicitly when using url() or when the form has no named action route:
$form = MyForm::make()->put();
$form = MyForm::make()->patch();
$form = MyForm::make()->delete();
$form = MyForm::make()->method('PUT');The shorthand methods post(), get(), put(), patch(), and delete() are all available.
Unsaved Changes Warning
Forms default to protected bool $unsavedWarning = false. Set it on the class when every instance should warn before the user leaves with unsaved changes:
class EditPostForm extends Form
{
protected bool $unsavedWarning = true;
public function fields(): array
{
return [
TextInput::make('title')->required(),
Textarea::make('body'),
Submit::make('Save'),
];
}
}Use unsavedWarning() when enabling it for a single instance:
$form = EditPostForm::make()->unsavedWarning();The browser's beforeunload confirmation appears when the user tries to close, reload, or leave the current document with unsaved changes. A successful submission resets the dirty baseline.
Data and Serialization
Getting Form Data
The data() method returns the current form data as a key-value array. For new forms this is the default values; for bound models it includes the model's attribute values:
$form = CreateUserForm::make();
$form->data();
// ['name' => null, 'email' => null]
$form = EditUserForm::make()->bind($user);
$form->data();
// ['name' => 'Jane Example', 'email' => 'jane@example.com']See Model Binding for how bind() resolves initial values.
Serialization
The toArray() method serializes the entire form to an array that Inertia passes to your frontend:
$form = CreateUserForm::make();
$form->toArray();A serialized form includes these top-level keys. The TextInput entry is shortened because each field type adds its own keys:
{
"action": "/users",
"method": "POST",
"fieldsets": [
{
"legend": null,
"description": null,
"class": null,
"id": null,
"columns": 1,
"fields": [
{
"component": "TextInput",
"name": "name",
"label": "Name",
"inputType": "text",
"required": true,
"precognitive": false,
"disabled": false
}
]
}
],
"data": {
"name": null,
"email": null
},
"dataAttributes": null,
"meta": null,
"unsavedWarning": false,
"wizard": null
}The frontend Form component reads this structure and renders the form automatically.
The wizard key is null for normal forms. When the form's wizard() method returns a WizardConfig or array, it holds the serialized wizard settings instead. See Wizard for the configuration API.
Data Attributes and Meta
Data Attributes
Add HTML data-* attributes to the form element:
$form = MyForm::make()
->dataAttribute('testId', 'create-user-form')
->dataAttribute('turbo', 'false');Keys are automatically converted to kebab-case with a data- prefix, so testId becomes data-test-id.
You may also set multiple attributes at once:
$form = MyForm::make()->dataAttributes([
'testId' => 'create-user-form',
'module' => 'users',
]);Meta
Attach arbitrary metadata when frontend code needs application-specific context from the serialized form config:
$form = MyForm::make()->meta([
'help_url' => 'https://docs.example.com/forms',
'version' => 2,
]);Unlike data attributes, meta values are not rendered as HTML attributes. They stay in the serialized form config for frontend code to read.
Each meta() call replaces the previously assigned application meta array. Some fields also contribute generated option data under meta.options; that generated data is merged into an existing meta.options array during serialization.
Extending with Macros
Both Form and Field use Laravel's Macroable trait, so you may add your own methods:
use InertiaUI\Forms\Form;
Form::macro('forCurrentTeam', function () {
return $this->meta([
'team_id' => auth()->user()->current_team_id,
]);
});
// Usage
$form = CreateProjectForm::make()->forCurrentTeam();use InertiaUI\Forms\Fields\TextInput;
TextInput::macro('phoneNumber', function () {
return $this->tel()->mask('(999) 999-9999');
});
// Usage
TextInput::make('phone')->phoneNumber();Conditional Chaining
All forms and fields support Laravel's Conditionable trait via the when() and unless() methods:
public function fields(): array
{
return [
TextInput::make('name')->required(),
TextInput::make('company')
->when($this->model instanceof Business, fn ($field) => $field->required()),
Toggle::make('is_admin')
->unless(auth()->user()->isAdmin(), fn ($field) => $field->disabled()),
];
}when() and unless() run while PHP builds the form schema. Use them to conditionally call fluent methods such as required() or disabled().
For conditions based on current form data, use visibleWhen() and hiddenWhen(). For permissions, use authorizedWhen() and authorizedUnless() so unauthorized fields are not serialized, included in data, or validated.
Tapping
Use tap() when a form or field needs setup from a helper, but you still want to keep chaining:
TextInput::make('billing_email')
->email()
->tap(fn (TextInput $field) => $this->configureBillingEmail($field))
->required();