Reference

PHP API

Form lifecycle, validation, authorization, wizards, uploads, and rich-text helpers.

The PHP package owns the form schema, initial data, authorization, Laravel validation, and upload resolution. A form is a regular container-resolved class extending Inertify\Form\Form.

namespace App\Forms;

use Inertify\Form\Fields\TextInput;
use Inertify\Form\Form;

final class ProfileForm extends Form
{
    public function fields(): array
    {
        return [
            TextInput::make('name')->required(),
            TextInput::make('email')->email()->required(),
        ];
    }
}

Form lifecycle

MethodContract
Form::make(array $parameters = [])Resolves the concrete form through Laravel's container and supplies the current request when available. Constructor parameters can be passed by name.
fields()Return a list of Field and Fieldset instances. Override this in the application form.
getFieldsets()Returns normalized fieldsets. Consecutive top-level fields are wrapped in an implicit fieldset.
route(string $name, array $parameters = [])Uses a named Laravel route as the action. The first non-HEAD route verb becomes the serialized method.
url(string $url)Uses a raw action URL.
method(string $method)Sets GET, POST, PUT, PATCH, or DELETE; other values throw.
get(), post(), put(), patch(), delete()Set the method and optionally a raw URL.
bind(Model|array $source, array $except = [])Reads initial values from a model or array and omits the listed paths.
data(?array $data = null)With an array, overrides initial values. With no argument, returns resolved initial data.
resolvedData()Returns normalized, authorized initial data after binding, explicit data, defaults, and nested defaults are applied.
unsavedWarning(bool $enabled = true)Enables the Vue navigation guard when the form is dirty.
scrollToFirstError(bool $enabled = true)Enables scrolling to the first registered field element after submission errors.
setRequest(Request $request)Explicitly sets the request used by validation, authorization closures, and request-aware options.
toArray() / jsonSerialize()Produces the stable form resource.
toInertiaProperty(PropertyContext $context)Receives the owning Inertia prop key and request, then serializes the resource. Normally Inertia calls this automatically.
getPropertyKey()Returns the owning Inertia prop key after contextual serialization, otherwise null.

A form may declare defaults as protected properties:

final class ProfileForm extends Form
{
    protected ?string $actionRoute = 'profiles.update';
    protected bool $unsavedWarning = true;
    protected bool $scrollToFirstError = true;
}

route() or url() overrides a declared action route. For the boolean flags, a declared property is authoritative; use either the protected-property style or the fluent methods consistently. Prefer route() when route parameters are required.

Initial-data precedence

For every authorized top-level field, initial data is resolved in this order:

  1. A value read by bind() when model binding is enabled for the field.
  2. A matching value supplied to data([...]).
  3. The field's default() value.
  4. The field's canonical empty value.

Only declared, authorized fields are retained. Nested repeater and block defaults are applied using dotted paths. Submit fields never enter form data.

$form = ProfileForm::make()
    ->bind($profile, except: ['internal_note'])
    ->data(['timezone' => 'Europe/Kyiv'])
    ->route('profiles.update', ['profile' => $profile])
    ->patch();

Inertia response

Forms implement ProvidesInertiaProperty, so pass the instance directly to an Inertia response:

use Inertia\Inertia;
use Inertia\Response;

public function edit(Profile $profile): Response
{
    return Inertia::render('Profiles/Edit', [
        'form' => ProfileForm::make()
            ->bind($profile)
            ->route('profiles.update', ['profile' => $profile]),
    ]);
}

Contextual serialization matters for query-backed comboboxes: it records the owning prop and allows the Vue controller to reload only that prop.

Validation

MethodContract
rules()Returns the generated Laravel rule map. Override to customize the full form-level map only when needed.
messages()Override to return custom validation messages.
attributes()Override to return custom validation attribute names.
validate(?array $data = null)Authorizes, validates request data or the supplied array, filters undeclared fields, and caches the result.
validated(bool $files = true)Returns the cached result or validates now, then applies field transformations. Pass files: false to remove every File, composer attachments, and rich-text image token lists.

Use the contextual #[Validate] attribute for controller injection:

use Illuminate\Http\RedirectResponse;
use Inertify\Form\Validate;

public function update(
    #[Validate] ProfileForm $form,
    Profile $profile,
): RedirectResponse {
    $profile->update($form->validated(files: false));

    return back();
}

The parameter must be typed as Form or a form subclass. Laravel resolves it, attaches the current request, authorizes it, and validates it before invoking the controller.

Hidden or disabled fields receive Laravel's exclude rule. Unauthorized fields never receive rules. Nested repeater, block, choice, date-range, slider-range, key-value, composer, rich-text image, and file-item rules are generated at their qualified paths.

The serialized rules array is renderer metadata, not the security boundary. Validation and authorization run in Laravel. Do not accept a client-modified schema or infer permission from client visibility.

Authorization and metadata

Form, Fieldset, Field, and BlockSet support authorize(), authorizedWhen(), and authorizedUnless() where applicable. Closures are container-aware through the package value resolver.

public function fields(): array
{
    return [
        TextInput::make('billing_code')
            ->authorize(fn () => request()->user()?->can('editBilling')),
    ];
}

An unauthorized form serializes as the exact empty resource by default and always throws when validation is attempted. Set inertia-forms.authorization.throw_on_unauthorized to true to throw during serialization too.

dataAttribute() normalizes a key to data-kebab-case. dataAttributes() sets several attributes. meta() stores arbitrary serializable consumer metadata. These methods exist on the form and relevant schema objects.

ProfileForm::make()
    ->dataAttribute('testId', 'profile-form')
    ->meta('version', 2);

Both Form and Field are macroable and support Laravel's when() and tap() helpers.

Wizards

Override wizard() or call withWizard():

use Inertify\Form\WizardConfig;

public function wizard(): WizardConfig
{
    return WizardConfig::make()
        ->step('Identity', 'Account details')
        ->step('Profile', 'Public information')
        ->validateOnStep()
        ->labels(next: 'Continue', prev: 'Back', submit: 'Save');
}

WizardConfig exposes:

  • make(array $steps = []), steps(), and step()
  • enabled() and allowSkip()
  • validateOnStep()
  • labels(), nextLabel(), prevLabel(), and submitLabel()

With no configured steps, each visible authorized fieldset becomes a step. Sequential array metadata is matched by fieldset index. A step may instead be selected with a Fieldset instance or integer fieldset index. Hidden fieldsets and fieldsets without a visible authorized child are omitted.

Upload resolution

After validation, secure upload tokens can be resolved from the form:

$data = $form->validated(files: false);

if ($avatar = $form->upload('avatar')) {
    $data['avatar_path'] = $avatar->store('avatars', 'public');
}

foreach ($form->uploads('documents') as $upload) {
    $upload->store('documents', 'private');
}

upload(string $name) returns one SubmittedUpload or null. uploads(string $name) returns submitted uploads in client order. The equivalent request macros are:

$request->formUpload('avatar');          // ?SubmittedUpload
$request->orderedFormUploads('gallery'); // Illuminate\Support\Collection

SubmittedUpload exposes:

MethodResult
isNew() / isExisting()Distinguishes a newly uploaded token from a retained existing-file token.
getKey()Returns the encrypted submitted token.
getIdentifier(), getName(), getMimeType(), getSize(), getPath(), getDisk()Reads verified upload metadata.
getUploadedFile()Returns a materialized Laravel uploaded file for new temporary or chunked content.
getRemoteFile()Returns a RemoteFile for a completed direct upload.
getExistingFile()Returns an ExistingFile for retained existing content.
store() / storeAs()Promotes a new upload. Existing files cannot be stored again through these methods.

store() and storeAs() delete package temporary content after promotion by default. Pass deleteTemporary: false only when coordinating an operation that must clean up after all related work succeeds.

Native storeWithForm() files remain Laravel UploadedFile values in validated() and in the request file bag; they are not returned by the token resolver.

Existing files

Serialize retained files with ExistingFile:

use Inertify\Form\Uploads\ExistingFile;

File::make('document')->existingFiles([
    ExistingFile::fromDisk(
        'private',
        'documents/report.pdf',
        expiration: 300,
    ),
]);

Public constructors are fromDisk(), fromFilesystem(), fromMediaLibrary(), and fromMediaLibraryWithoutPreview(). The resulting key is encrypted and expiring. Preview generation first attempts a temporary URL and then a regular filesystem URL.

An existing-file token proves the package created that descriptor; it does not decide whether the current user may retain, delete, or reorder the underlying model attachment. Enforce that authorization in the application operation that consumes the form.

Media Library adapter

Spatie Media Library is optional. For a compatible model, synchronize retained, added, removed, and reordered items after validation:

use Inertify\Form\Uploads\MediaLibraryUploads;

$media = MediaLibraryUploads::syncCollection(
    request: request(),
    model: $profile,
    field: 'gallery',
    collection: 'gallery',
    disk: 'public',
);

The adapter detects the public Media Library methods at runtime and throws when a model is incompatible. It does not install or configure Media Library.

Rich-text helpers

RichText::imageUploads() creates a companion token list named <field>_images. The application-owned editor must place each matching token on its image as data-inertia-forms-upload.

Store and rewrite the images after form validation:

use Inertify\Form\RichText\RichTextImage;
use Inertify\Form\RichText\RichTextUploads;
use Inertify\Form\Uploads\SubmittedUpload;

$html = RichTextUploads::from(request(), 'body')
    ->storeImagesUsing(function (
        SubmittedUpload $upload,
        RichTextImage $image,
    ): RichTextImage {
        $path = $upload->store(
            'post-images',
            'private',
            deleteTemporary: false,
        );

        return $image->identifier($path, ['disk' => 'private']);
    })
    ->keepTokenized()
    ->toHtml();

RichTextUploads verifies that the companion list and HTML markers contain the same unique token set, rewrites image attributes, and deletes temporary tokens only after the complete rewrite. Token order and duplicate entries are ignored. storeImagesInMediaLibrary() is the optional adapter shortcut.

For stored tokenized HTML, generate fresh delivery URLs with RichTextContent:

use Illuminate\Support\Facades\Storage;
use Inertify\Form\RichText\RichTextContent;

$html = RichTextContent::from($post->body)
    ->replaceImagesUsing(function ($stored, $image) {
        $disk = (string) $stored->meta('disk', 'private');

        return $image->src(
            Storage::disk($disk)->temporaryUrl(
                $stored->identifier(),
                now()->addMinutes(5),
            ),
        );
    })
    ->toHtml();

RichTextImage supports src(), alt(), title(), width(), height(), dimensions(), identifier(), attribute(), attributes(), and toAttributes(). The stored-image callback receives a RichTextStoredImage with identifier(), metadata(), meta(), and attributes().

Rich-text helpers verify upload-token integrity and marker correspondence. They do not sanitize HTML. Sanitize untrusted content according to your application's allow-list before rendering it.
Copyright © 2026 Inertify · Released under the MIT License