Reference

Resource shape

Stable Laravel-to-Vue resource contracts for forms, fields, conditions, wizards, choices, and uploads.

The form resource is the public wire contract between Laravel and @inertify/form-vue. Field-specific properties are additive; consumers should read the keys they understand and ignore unknown keys.

Use the exported TypeScript types rather than duplicating the contract:

import type {
  FormField,
  FormFieldInstance,
  FormFieldset,
  FormResource,
  FormUploadDescriptor,
  WizardResource,
} from '@inertify/form-vue'

Top-level form

Every authorized form has these top-level keys:

{
  "action": "/profiles/1",
  "method": "PATCH",
  "fieldsets": [],
  "data": {},
  "dataAttributes": null,
  "meta": null,
  "unsavedWarning": true,
  "scrollToFirstError": true,
  "wizard": null
}
KeyShapeMeaning
actionstring | nullSubmission URL. A form without an action can still be used for local state.
methodGET | POST | PUT | PATCH | DELETEUppercase submission method.
fieldsetsFormFieldset[]Authorized schema groups in declaration order.
dataobjectAuthorized initial values, normalized defaults, and existing-file descriptors. An empty value serializes as {}, not [].
dataAttributesobject or nullApplication-owned form data attributes with normalized data-* keys.
metaobject or nullApplication metadata plus generated query-backed combobox option pages when needed.
unsavedWarningbooleanEnables the browser navigation confirmation while dirty.
scrollToFirstErrorbooleanEnables first-error scrolling when an application input registered its element.
wizardobject or nullSerialized wizard behavior and visible steps.

An unauthorized form returns the same keys with action: null, method: "POST", empty fieldsets/data, both flags false, and nullable values set to null. When authorization.throw_on_unauthorized is enabled, serialization throws instead.

Fieldsets

{
  "id": "identity",
  "legend": "Identity",
  "description": "Account details",
  "fields": [],
  "visibility": null,
  "clearWhenHidden": false,
  "dataAttributes": null,
  "meta": null
}

Fieldsets do not create a value path. They may contain fields or nested fieldsets. Unauthorized children are omitted. A null ID is resolved by Vue to a deterministic index-based identifier.

Base field

All authorized fields begin with this shape and add their behavior-specific options:

{
  "component": "Text",
  "name": "email",
  "label": "Email",
  "default": null,
  "help": null,
  "placeholder": null,
  "rules": ["required", "string", "email"],
  "required": true,
  "nullable": false,
  "precognitive": true,
  "disabled": false,
  "readonly": false,
  "autofocus": false,
  "modelBinding": true,
  "visibility": null,
  "clearWhenHidden": false,
  "dataAttributes": null,
  "meta": null,
  "inputType": "email",
  "clearable": false,
  "copyable": false,
  "viewable": false
}
KeyContract
componentExact semantic renderer discriminator. It is a frontend protocol key, not an HTML element or PHP class name.
nameCanonical schema name. Nested schemas keep their relative name here.
label, help, placeholderResolved functional content.
defaultResolved field default, or null when no default was declared. Initial current data lives in top-level data.
rulesJSON-safe descriptions of Laravel rules. Object rules are represented by class name.
required, nullable, precognitive, disabled, readonly, autofocusResolved semantic state for the current resource request.
modelBindingWhether server initial-value binding is enabled for the field.
visibilityA condition, group, literal boolean, or null.
clearWhenHiddenWhether Vue clears the value on a visible-to-hidden transition.
dataAttributes, metaConsumer-owned serializable values.

There is no authorized property in a normal PHP resource: unauthorized fields are absent entirely. The TypeScript shape accepts compatibility keys from compatible producers, but applications should not depend on them being emitted here.

Serialized rules are descriptive. They may contain a PHP class name for a custom Laravel rule and cannot reproduce server validation in JavaScript. Treat Laravel validation as authoritative.

Qualified field instances

The server schema stores relative child names. Vue resolves one FormFieldInstance for every concrete row:

interface FormFieldInstance extends FormField {
  path: string             // "projects.0.title"
  schemaName: string       // "title"
  schemaField: FormField   // original serialized schema object
  rowPath: string | null   // "projects.0"
  fieldsetIndex: number
  ancestorVisibility: FormVisibilityCondition[]
  ancestorClearWhenHidden: boolean
}

Use path for input names, errors, slots, element registration, and composable calls. Use schemaField to inspect the original relative declaration.

Visibility conditions

A simple condition serializes as:

{
  "field": "is_employed",
  "operator": "=",
  "value": true,
  "dependsOn": ["is_employed"]
}

Unary operators (empty, not_empty, truthy, and falsy) omit value.

Groups serialize recursively:

{
  "mode": "and",
  "conditions": [
    {
      "field": "kind",
      "operator": "=",
      "value": "business",
      "dependsOn": ["kind"]
    },
    {
      "mode": "not",
      "conditions": [
        {
          "field": "archived",
          "operator": "truthy",
          "dependsOn": ["archived"]
        }
      ],
      "dependsOn": ["archived"]
    }
  ],
  "dependsOn": ["kind", "archived"]
}

Group modes emitted by PHP are and, or, and not. A path beginning with $. is always root-relative; otherwise Vue and PHP first allow a current collection row to satisfy the dependency.

Visibility state is still client-observable and client-modifiable. Never use it in place of authorization.

Nested schemas

Repeater

{
  "component": "Repeater",
  "name": "projects",
  "schema": [
    {
      "component": "Text",
      "name": "title",
      "label": "Title"
    }
  ],
  "defaultItem": {
    "title": null
  },
  "minItems": 1,
  "reorderable": true
}

The abbreviated child above also contains every base field key. Current rows live at data.projects; defaultItem is the application-ready template used when appending.

Blocks

{
  "component": "Blocks",
  "name": "content",
  "sets": [
    {
      "type": "hero",
      "label": "Hero",
      "description": "Page hero",
      "maxItems": 1,
      "defaultData": {
        "heading": "Welcome"
      },
      "schema": [],
      "dataAttributes": null,
      "meta": null
    }
  ]
}

Current rows use one canonical data shape:

[
  {
    "type": "hero",
    "data": {
      "heading": "Welcome"
    }
  }
]

Laravel validates type against authorized block sets and validates data against the selected set's schema.

Key-value

KeyValue emits mode, keyRules, and valueRules, plus configured labels and limits. Keyed mode uses an object; single mode uses a list. Vue renderers may project either shape into editable rows, but must submit the canonical configured shape.

Choices

Finite Radio, CheckboxGroup, and Combobox options use normalized records:

{
  "value": 1,
  "label": "Administrator",
  "description": "Full access",
  "disabled": false,
  "disabledReason": null
}

Combobox records may additionally contain group, image, avatar, badge, url, metadata, and selectedSuffix.

An application JSON endpoint may return a plain option array, a paginator-shaped object whose data contains options, or a supported continuation shape. Record creation may return one option directly or as { "item": option }.

Query-backed option metadata

Request-aware Eloquent/query choices do not embed the full result set in the field. The field points to its page under form metadata:

{
  "meta": {
    "options": {
      "assignee_id": {
        "data": [
          { "value": 1, "label": "Ada" }
        ],
        "current_page": 1,
        "per_page": 25,
        "total": 80,
        "last_page": 4,
        "next_page_url": "/profiles?page=2"
      }
    }
  }
}

The corresponding field includes:

{
  "options": [],
  "optionsKey": "assignee_id",
  "optionsMode": "inertia",
  "selected": [
    { "value": 42, "label": "Selected person" }
  ]
}

Nested option keys use schema paths with * placeholders. The Vue combobox controller owns targeted partial reloads and resolves each concrete field against this metadata.

Wizard

{
  "enabled": true,
  "allowSkip": false,
  "validateOnStep": true,
  "steps": [
    {
      "fieldset": 0,
      "title": "Identity",
      "description": "Account details"
    }
  ],
  "nextLabel": "Continue",
  "prevLabel": "Back",
  "submitLabel": "Save"
}

fieldset is the original fieldset index. Hidden or unauthorized fieldsets and fieldsets without a visible authorized child are omitted from steps. Labels may be null; Vue supplies its normal Next, Previous, and Submit fallbacks.

Upload descriptor

Package-managed file fields expose both flat compatibility properties and a nested descriptor. New renderers should prefer upload:

{
  "strategy": "chunked",
  "endpoints": {
    "destroy": {
      "method": "DELETE",
      "url": "/_inertia-forms/file-upload"
    },
    "chunked": {
      "start": {
        "method": "POST",
        "url": "/_inertia-forms/file-upload/chunked/start"
      },
      "status": {
        "method": "GET",
        "url": "/_inertia-forms/file-upload/chunked/status"
      },
      "append": {
        "method": "POST",
        "url": "/_inertia-forms/file-upload/chunked/chunk"
      },
      "complete": {
        "method": "POST",
        "url": "/_inertia-forms/file-upload/chunked/complete"
      },
      "abort": {
        "method": "DELETE",
        "url": "/_inertia-forms/file-upload/chunked/abort"
      }
    }
  },
  "limits": {
    "maxSizeKiB": 10240,
    "chunkSizeBytes": 5242880,
    "directMaxSizeKiB": 5242880,
    "partSizeBytes": 16777216,
    "multipartThresholdBytes": 104857600
  },
  "disk": null,
  "rulesToken": "encrypted-expiring-token",
  "requiresRulesToken": true
}

strategy is temporary, form, chunked, or direct. Endpoint sets vary by strategy. form has no package endpoints.

The same field may include storeWithForm, temporaryUploadUrl, temporaryUploadDeleteUrl, chunked, chunkSize, chunkedUrls, directToStorage, uploadDisk, uploadPartSize, uploadMultipartThreshold, directUploadUrls, uploadRulesToken, and requiresUploadRulesToken for compatibility.

Uploaded and existing files

Upload responses and existing-file values contain a stable client key plus display metadata. Existing files include compatibility aliases:

{
  "key": "encrypted-expiring-token",
  "id": "private:documents/report.pdf",
  "identifier": "private:documents/report.pdf",
  "filename": "report.pdf",
  "name": "report",
  "previewUrl": "https://example.test/temporary-preview",
  "preview_url": "https://example.test/temporary-preview",
  "mimeType": "application/pdf",
  "mime_type": "application/pdf",
  "size": 12345,
  "size_in_bytes": 12345,
  "metadata": {}
}

The encrypted key, not the display metadata, is the submitted reference resolved by Laravel. Applications must still authorize the operation against their own model.

Composer and rich text

Without attachments, Composer data is a string or null. With allowAttachments(), it is:

{
  "text": "Message body",
  "attachments": ["encrypted-upload-token"]
}

RichText data remains an HTML string. Image uploads add a separate sibling list:

{
  "body": "<p>Hello</p><img data-inertia-forms-upload=\"encrypted-upload-token\">",
  "body_images": ["encrypted-upload-token"]
}

The HTML marker list and companion list must contain the same unique token set; order and duplicate entries are ignored. The PHP rich-text helper verifies correspondence before storage, but HTML sanitation remains the application's responsibility.

Compatibility guidance

  • Switch on component, not a PHP class name guessed from a label or value.
  • Treat unknown field properties and unknown option-record properties as additive.
  • Use generated qualified path values in Vue rather than concatenating nested names yourself.
  • Use public package types and entry points; do not couple renderers to package implementation modules.
  • Never infer server authorization from the presence, visibility, disabled state, metadata, or client rules of a field.
Copyright © 2026 Inertify · Released under the MIT License