Skip to content
Core Concepts

Model Binding ​

Fill a form with existing values from an Eloquent model or an array with bind().

php
public function edit(Post $post): Response
{
    return Inertia::render('Posts/Edit', [
        'form' => PostForm::make()
            ->route('posts.update', $post)
            ->bind($post),
    ]);
}

When to use: edit screens. One form class can handle both "create" and "edit".

Examples ​

Each example is a live form built from the PHP below it. The docs have no database, so the examples bind arrays; an Eloquent model works the same way.

Basic ​

title and excerpt come from the bound array. author isn't in it, so the field falls back to its default.

php
use Erag\InertiaForms\Fields\Submit;
use Erag\InertiaForms\Fields\Textarea;
use Erag\InertiaForms\Fields\TextInput;
use Erag\InertiaForms\Form;

class PostForm extends Form
{
    public function fields(): array
    {
        return [
            TextInput::make('title')->required(),
            Textarea::make('excerpt')->rows(2),
            TextInput::make('author')->default('Editorial team'),
            Submit::make('Save post'),
        ];
    }
}

PostForm::make()->bind([
    'title' => 'Shipping forms from PHP',
    'excerpt' => 'One class describes the fields, the rules and the layout.',
]);

Nested keys ​

Dot-notation names read nested values, such as settings stored in a JSON column. billing.email is present but null, so it starts empty instead of using its default.

php
use Erag\InertiaForms\Fields\Combobox;
use Erag\InertiaForms\Fields\Fieldset;
use Erag\InertiaForms\Fields\Submit;
use Erag\InertiaForms\Fields\TextInput;
use Erag\InertiaForms\Form;

class SettingsForm extends Form
{
    public function fields(): array
    {
        return [
            TextInput::make('team_name')->required(),
            Fieldset::make('Address')->columns(2)->fields([
                TextInput::make('address.street')->columnSpan(2),
                TextInput::make('address.city'),
                Combobox::make('address.country')->options(['IN' => 'India', 'US' => 'United States']),
            ]),
            TextInput::make('billing.email')->email()->default('billing@example.com'),
            Submit::make('Save settings'),
        ];
    }
}

// For example $team->settings, stored as JSON.
SettingsForm::make()->bind([
    'team_name' => 'Acme',
    'address' => ['street' => '12 MG Road', 'city' => 'Bengaluru', 'country' => 'IN'],
    'billing' => ['email' => null],
]);

Advanced: enums, dates and edit mode ​

The bound values are the kind a model with casts returns: a backed enum, Carbon dates, a collection and an integer flag. Each field converts its value for the browser. Because a model is bound, getModel() switches the button to "Save changes".

php
use Erag\InertiaForms\Fields\CheckboxGroup;
use Erag\InertiaForms\Fields\DatePicker;
use Erag\InertiaForms\Fields\Radio;
use Erag\InertiaForms\Fields\Submit;
use Erag\InertiaForms\Fields\TextInput;
use Erag\InertiaForms\Fields\TimePicker;
use Erag\InertiaForms\Fields\Toggle;
use Erag\InertiaForms\Form;
use Illuminate\Support\Carbon;

enum PostStatus: string
{
    case Draft = 'draft';
    case Published = 'published';
    case Archived = 'archived';
}

class EditPostForm extends Form
{
    public function fields(): array
    {
        $editing = $this->getModel() !== null;

        return [
            TextInput::make('title')->required(),
            Radio::make('status')->options(PostStatus::class)->buttons()->required(),
            DatePicker::make('published_at')->withTime(),
            TimePicker::make('reminder_at')->label('Daily reminder'),
            CheckboxGroup::make('tags')->inline()->options(['laravel', 'inertia', 'vue', 'php']),
            Toggle::make('featured'),
            Submit::make($editing ? 'Save changes' : 'Create post'),
        ];
    }
}

// The values an Eloquent model with casts would return.
EditPostForm::make()->bind([
    'title' => 'Forms without the boilerplate',
    'status' => PostStatus::Published,
    'published_at' => Carbon::parse('2026-09-27 10:30:00'),
    'reminder_at' => Carbon::parse('2026-09-27 08:00:00'),
    'tags' => collect(['laravel', 'inertia']),
    'featured' => 1,
]);

Where initial values come from ​

For each field, data() picks the first of these that applies:

  1. The bound model or array, when it has a value for the field name.
  2. The field's default from ->default().
  3. The field's empty value (for example '' for text, null for a combobox, [] for a checkbox group).
php
TextInput::make('title')->default('Untitled');

PostForm::make()->data();                         // ['title' => 'Untitled']
PostForm::make()->bind(['title' => 'Hi'])->data(); // ['title' => 'Hi']

What counts as "has a value" ​

  • Arrays: the key exists (checked with Arr::has(), dot notation supported). A key set to null counts as present, so the default is not used; the field gets its empty value.
  • Models: the value is not null, or the first segment of the name is one of the model's attributes. Accessors and loaded relations work through data_get().

Nested data ​

Dot-notation field names read nested values and produce nested form data.

php
TextInput::make('address.city');

PostForm::make()->bind(['address' => ['city' => 'Delhi']])->data();
// ['address' => ['city' => 'Delhi']]

With a model, address.city works with a JSON column cast to an array, or with a loaded address relation.

Value conversion ​

Bound values are converted to what each control expects:

FieldConversion
All fieldsA backed enum becomes its value. null becomes the empty value.
DatePickerDateTimeInterface (Carbon) becomes Y-m-d, or Y-m-d\TH:i with withTime(). Strings are passed through unchanged.
TimePickerDateTimeInterface becomes H:i, or H:i:s with withSeconds().
Checkbox / ToggleTruthy becomes the true value, falsy the false value (1 → true).
Combobox multiple() / CheckboxGroupCollections and arrays become a plain list. Enum items become their values.
FileUploadAlways starts empty. Existing files are never sent to the browser.

Date columns

Add a date or datetime cast to your model so the value arrives as Carbon and is formatted for the input. A raw string like 2026-09-27 10:30:00 is passed through as-is.

Binding an array ​

bind() also accepts a plain array. This is handy for settings stored as JSON, or data from an API.

php
SettingsForm::make()->bind($team->settings);

Reading the bound model ​

Inside your form, $this->getModel() returns whatever was bound. Use it to tweak fields for edit mode:

php
public function fields(): array
{
    $editing = $this->getModel() !== null;

    return [
        TextInput::make('email')->email()->required()->readonly($editing),
        TextInput::make('password')->password()->required()->authorizedUnless($editing),
        Submit::make($editing ? 'Save changes' : 'Create account'),
    ];
}

Model binding does not save anything

bind() only sets initial values. Saving is up to you:

php
public function update(Request $request, Post $post)
{
    $post->update(PostForm::make()->bind($post)->validate($request));

    return to_route('posts.index');
}