Skip to content

Conversation

@pascalbaljet
Copy link
Member

@pascalbaljet pascalbaljet commented Jul 31, 2025

Form Component

Basic Usage

At its simplest, the <Form> component behaves much like a classic HTML form:

<script setup>
import { Form } from '@inertiajs/vue3'
</script>

<template>
  <Form action="/users" method="post">
    <input type="text" name="name" />
    <input type="email" name="email" />
    <button type="submit">Create User</button>
  </Form>
</template>

It also supports more advanced use cases, including nested data structures and file uploads:

<template>
  <Form action="/reports" method="post">
    <input type="text" name="name" />
    <textarea name="report[description]"></textarea>
    <input type="text" name="report[tags][]" />
    <input type="text" name="report[tags][]" />
    <input type="text" name="report[tags][]" />
    <input type="file" name="documents" multiple />
    <button type="submit">Create Report</button>
  </Form>
</template>

Form data is gathered using a FormData instance and then converted to a plain JavaScript object. This lets the form track its dirty state and, when submitting a GET request, merge the data into the query string automatically.

You can also pass a transform prop to modify the form data before submission. This is great for injecting extra fields, although hidden inputs work too:

<template>
  <Form
    action="/posts"
    method="post"
    :transform="data => ({
      ...data,
      user_id: props.user.id,
    })"
  >
    <input type="text" name="title" />
    <textarea name="content"></textarea>
    <button type="submit">Create Post</button>
  </Form>
</template>

Slot Props

Just like the useForm() helper, the <Form> component exposes reactive state and helpers through its default slot:

<template>
  <Form
    action="/posts"
    method="post"
    #default="{
      errors,
      hasErrors,
      processing,
      progress,
      wasSuccessful,
      recentlySuccessful,
      setError,
      clearErrors,
      isDirty,
      reset,
      submit,
    }"
    class="mt-6 max-w-2xl space-y-6"
  >
    <input type="text" name="name" />
    <div v-if="errors.name">{{ errors.name }}</div>

    <input type="email" name="email" />
    <div v-if="errors.email">{{ errors.email }}</div>

    <button type="submit" :disabled="processing">
      {{ processing ? 'Creating...' : 'Create User' }}
    </button>

    <div v-if="wasSuccessful">User created successfully!</div>
  </Form>
</template>

Options

In addition to action and method, the <Form> component accepts several props. Many of them are identical to the ones available in Inertia's VisitOptions:

<template>
  <Form
    action="/profile"
    method="put"
    error-bag="profile"
    query-string-array-format="indices"
    :headers="{ 'X-Custom-Header': 'Demo-Value' }"
    :show-progress="false"
    :options="{
      preserveScroll: true,
      preserveState: true,
      preserveUrl: true,
      replace: true,
      only: ['users', 'flash'],
      except: ['secret'],
      reset: ['page'],
    }"
  />
</template>

Some props are intentionally grouped under options instead of being top-level, to avoid confusion. For example, only, except, and reset relate to partial reloads, not partial submissions. The general rule: top-level props are for the form submission itself, while options control how Inertia handles the next visit.

Events

The <Form> component emits the same events as useForm(), except the ones related to prefetching:

<template>
  <Form
    action="/users"
    method="post"
    @cancelToken="handleCancelToken"
    @before="handleBefore"
    @start="handleStart"
    @progress="handleProgress"
    @cancel="handleCancel"
    @success="handleSuccess"
    @error="handleError"
    @finish="handleFinish"
  />
</template>

@laserhybiz
Copy link

I think dots should also be supported for nested data

<template>
  <Form action="/reports" method="post">
    <input type="text" name="name" />
    <textarea name="report.description"></textarea>
    <input type="text" name="report.tags.0" />
    <input type="text" name="report.tags.1" />
    <input type="text" name="report.tags.2" />
    <input type="file" name="documents" multiple />
    <button type="submit">Create Report</button>
  </Form>
</template>

This is much easier to read and will also make it consistent with the error keys

@pascalbaljet
Copy link
Member Author

@laserhybiz Thanks, I'll look into that! Supporting that wouldn't be terribly hard, but I'm more concerned about people wanting to submit field names containing an actual literal period. Maybe we should support escaping it like the validator does: https://laravel.com/index.php/docs/12.x/validation#a-note-on-nested-attributes

@laserhybiz
Copy link

@pascalbaljet Thanks for your reply, never knew Laravel has this escaping syntax

@pascalbaljet
Copy link
Member Author

Alright, dotted keys are now supported along with escaping dots:

<input type="text" name="user.name" placeholder="User Name" />
<input type="text" name="user.profile.city" placeholder="City" />
<input type="text" name="config\.app\.name" placeholder="App Name" />

Also, the visitOptions prop has been renamed options.

@pascalbaljet pascalbaljet merged commit 02032e4 into master Aug 8, 2025
6 checks passed
@pascalbaljet pascalbaljet deleted the form-component branch August 8, 2025 08:10
@igorleszczynski
Copy link

Is it possible to integrate this form component with yup library for additional live frontend verification? That will be perfect for me. I'm currently using Formik but it's already irritating me. For complex forms of course.

@aalhoura
Copy link

aalhoura commented Aug 26, 2025

It would also be nice to migrate the Inertia useForm helper from the Laravel Precognition package to a dedicated <PrecognitiveForm> component if possible!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants