> ## Documentation Index
> Fetch the complete documentation index at: https://inertiajs-vi.tuantq.online/llms.txt
> Use this file to discover all available pages before exploring further.

# Form

export const VueSpecific = ({children}) => {
  const [code, setCode] = useState(() => {
    if (typeof window === "undefined") {
      return "Vue";
    }
    return localStorage.getItem("code")?.replace(/"/g, "") || "Vue";
  });
  useEffect(() => {
    const handler = event => {
      if (event.detail?.key === "code") {
        setCode(event.detail.value?.replace(/"/g, ""));
      }
    };
    window.addEventListener("localStorageUpdate", handler);
    return () => window.removeEventListener("localStorageUpdate", handler);
  }, []);
  if (code !== "Vue") {
    return null;
  }
  return children;
};

export const SvelteSpecific = ({children}) => {
  const [code, setCode] = useState(() => {
    if (typeof window === "undefined") {
      return null;
    }
    return localStorage.getItem("code")?.replace(/"/g, "") || null;
  });
  useEffect(() => {
    const handler = event => {
      if (event.detail?.key === "code") {
        setCode(event.detail.value?.replace(/"/g, ""));
      }
    };
    window.addEventListener("localStorageUpdate", handler);
    return () => window.removeEventListener("localStorageUpdate", handler);
  }, []);
  if (!code?.includes("Svelte")) {
    return null;
  }
  return children;
};

export const ReactSpecific = ({children}) => {
  const [code, setCode] = useState(() => {
    if (typeof window === "undefined") {
      return null;
    }
    return localStorage.getItem("code")?.replace(/"/g, "") || null;
  });
  useEffect(() => {
    const handler = event => {
      if (event.detail?.key === "code") {
        setCode(event.detail.value?.replace(/"/g, ""));
      }
    };
    window.addEventListener("localStorageUpdate", handler);
    return () => window.removeEventListener("localStorageUpdate", handler);
  }, []);
  if (code !== "React") {
    return null;
  }
  return children;
};

export const ClientSpecific = ({children}) => {
  const [nada, setNada] = useState();
  return children;
};

<Warning>Bạn đang xem tài liệu Inertia.js v2. Inertia.js v3 đã được phát hành và hiện là phiên bản mặc định. Hãy xem [hướng dẫn nâng cấp](/v3/getting-started/upgrade-guide) để bắt đầu.</Warning>

Inertia cung cấp hai cách chính để xây dựng form: component `<Form>` và helper `useForm`. Cả hai tích hợp với validation của framework phía máy chủ và xử lý submit form mà không tải lại toàn bộ trang.

## Component Form

Inertia cung cấp component `<Form>` hoạt động gần giống form HTML truyền thống nhưng sử dụng Inertia bên dưới để tránh tải lại toàn bộ trang. Đây là cách đơn giản nhất để bắt đầu làm việc với form trong Inertia.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <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>
  ```

  ```jsx React icon="react" theme={null}
  import { Form } from '@inertiajs/react'

  export default () => (
      <Form action="/users" method="post">
          <input type="text" name="name" />
          <input type="email" name="email" />
          <button type="submit">Create User</button>
      </Form>
  )
  ```

  ```svelte Svelte icon="s" theme={null}
  <script>
      import { Form } from '@inertiajs/svelte'
  </script>

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

<ClientSpecific>
  Tương tự form HTML truyền thống, bạn không cần gắn <VueSpecific>`v-model`</VueSpecific><ReactSpecific>handler `onChange`</ReactSpecific><SvelteSpecific>`bind:`</SvelteSpecific> vào input; chỉ cần đặt attribute `name` cho từng input <ReactSpecific>và `defaultValue` nếu phù hợp </ReactSpecific>, component `Form` sẽ xử lý việc gửi dữ liệu.
</ClientSpecific>

Component cũng hỗ trợ cấu trúc dữ liệu lồng nhau, tải file và dotted key notation.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <template>
      <Form action="/reports" method="post">
          <input type="text" name="name" />
          <textarea name="report[description]"></textarea>
          <input type="text" name="report[tags][]" />
          <input type="file" name="documents" multiple />
          <button type="submit">Create Report</button>
      </Form>
  </template>
  ```

  ```jsx React icon="react" theme={null}
  <Form action="/reports" method="post">
      <input type="text" name="name" />
      <textarea name="report[description]"></textarea>
      <input type="text" name="report[tags][]" />
      <input type="file" name="documents" multiple />
      <button type="submit">Create Report</button>
  </Form>
  ```

  ```svelte Svelte icon="s" theme={null}
  <Form action="/reports" method="post">
      <input type="text" name="name" />
      <textarea name="report[description]"></textarea>
      <input type="text" name="report[tags][]" />
      <input type="file" name="documents" multiple />
      <button type="submit">Create Report</button>
  </Form>
  ```
</CodeGroup>

Bạn có thể truyền prop `transform` để sửa dữ liệu form trước khi submit. Điều này hữu ích khi cần chèn field bổ sung hoặc transform dữ liệu hiện có, dù hidden input cũng có thể dùng được.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <template>
      <Form
          action="/posts"
          method="post"
          :transform="data => ({ ...data, user_id: 123 })"
      >
          <input type="text" name="title" />
          <button type="submit">Create Post</button>
      </Form>
  </template>
  ```

  ```jsx React icon="react" theme={null}
  <Form
      action="/posts"
      method="post"
      transform={data => ({ ...data, user_id: 123 })}
  >
      <input type="text" name="title" />
      <button type="submit">Create Post</button>
  </Form>
  ```

  ```svelte Svelte icon="s" theme={null}
  <Form
      action="/posts"
      method="post"
      transform={data => ({ ...data, user_id: 123 })}
  >
      <input type="text" name="title" />
      <button type="submit">Create Post</button>
  </Form>
  ```
</CodeGroup>

### Wayfinder

Khi dùng [Wayfinder](https://github.com/laravel/wayfinder), bạn có thể truyền object kết quả trực tiếp vào prop `action`. Component Form sẽ tự suy ra HTTP method và URL từ object Wayfinder.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <script setup>
  import { Form } from '@inertiajs/vue3'
  import { store } from 'App/Http/Controllers/UserController'
  </script>

  <template>
      <Form :action="store()">
          <input type="text" name="name" />
          <input type="email" name="email" />
          <button type="submit">Create User</button>
      </Form>
  </template>
  ```

  ```jsx React icon="react" theme={null}
  import { Form } from '@inertiajs/react'
  import { store } from 'App/Http/Controllers/UserController'

  export default () => (
      <Form action={store()}>
          <input type="text" name="name" />
          <input type="email" name="email" />
          <button type="submit">Create User</button>
      </Form>
  )
  ```

  ```svelte Svelte icon="s" theme={null}
  <script>
      import { Form } from '@inertiajs/svelte'
      import { store } from 'App/Http/Controllers/UserController'
  </script>

  <Form action={store()}>
      <input type="text" name="name" />
      <input type="email" name="email" />
      <button type="submit">Create User</button>
  </Form>
  ```
</CodeGroup>

### Giá trị mặc định

<ClientSpecific>
  Bạn có thể đặt giá trị mặc định cho input form bằng attribute HTML tiêu chuẩn. Dùng <ReactSpecific>`defaultValue`</ReactSpecific><VueSpecific>`defaultValue`</VueSpecific><SvelteSpecific>`value`</SvelteSpecific> cho text input và textarea, và <ReactSpecific>`defaultChecked`</ReactSpecific><VueSpecific>`defaultChecked`</VueSpecific><SvelteSpecific>`checked`</SvelteSpecific> cho checkbox và radio.
</ClientSpecific>

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <template>
      <Form action="/users" method="post">
          <input type="text" name="name" defaultValue="John Doe" />

          <select name="country">
              <option value="us">United States</option>
              <option value="ca">Canada</option>
              <option value="uk" selected>United Kingdom</option>
          </select>

          <input type="checkbox" name="subscribe" value="yes" defaultChecked />

          <button type="submit">Submit</button>
      </Form>
  </template>
  ```

  ```jsx React icon="react" theme={null}
  <Form action="/users" method="post">
      <input type="text" name="name" defaultValue="John Doe" />

      <select name="country" defaultValue="uk">
          <option value="us">United States</option>
          <option value="ca">Canada</option>
          <option value="uk">United Kingdom</option>
      </select>

      <input type="checkbox" name="subscribe" value="yes" defaultChecked />

      <button type="submit">Submit</button>
  </Form>
  ```

  ```svelte Svelte icon="s" theme={null}
  <Form action="/users" method="post">
      <input type="text" name="name" value="John Doe" />

      <select name="country" value="uk">
          <option value="us">United States</option>
          <option value="ca">Canada</option>
          <option value="uk">United Kingdom</option>
      </select>

      <input type="checkbox" name="subscribe" value="yes" checked />

      <button type="submit">Submit</button>
  </Form>
  ```
</CodeGroup>

### Input checkbox

Khi làm việc với checkbox, bạn có thể muốn thêm attribute `value` rõ ràng như `value="1"`. Nếu không có value, checkbox được chọn sẽ submit thành `"on"`, giá trị mà một số validation rule phía máy chủ có thể không nhận diện là boolean hợp lệ.

### Slot props

Component `<Form>` expose reactive state và các helper method qua default slot, cho phép truy cập trạng thái xử lý form, lỗi và utility function.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <template>
      <Form
          action="/users"
          method="post"
          #default="{
              errors,
              hasErrors,
              processing,
              progress,
              wasSuccessful,
              recentlySuccessful,
              setError,
              clearErrors,
              resetAndClearErrors,
              defaults,
              isDirty,
              reset,
              submit,
          }"
      >
          <input type="text" name="name" />

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

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

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

  ```jsx React icon="react" theme={null}
  <Form action="/users" method="post">
      {({
          errors,
          hasErrors,
          processing,
          progress,
          wasSuccessful,
          recentlySuccessful,
          setError,
          clearErrors,
          resetAndClearErrors,
          defaults,
          isDirty,
          reset,
          submit,
      }) => (
          <>
              <input type="text" name="name" />

              {errors.name && <div>{errors.name}</div>}

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

              {wasSuccessful && <div>User created successfully!</div>}
          </>
      )}
  </Form>
  ```

  ```svelte Svelte 4 icon="s" theme={null}
  <Form
      action="/users"
      method="post"
      let:errors
      let:hasErrors
      let:processing
      let:progress
      let:wasSuccessful
      let:recentlySuccessful
      let:setError
      let:clearErrors
      let:resetAndClearErrors
      let:defaults
      let:isDirty
      let:reset
      let:submit
  >
      <input type="text" name="name" />

      {#if errors.name}
          <div>{errors.name}</div>
      {/if}

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

      {#if wasSuccessful}
          <div>User created successfully!</div>
      {/if}
  </Form>
  ```

  ```svelte Svelte 5 icon="s" theme={null}
  <Form action="/users" method="post">
      {#snippet children({
          errors,
          hasErrors,
          processing,
          progress,
          wasSuccessful,
          recentlySuccessful,
          setError,
          clearErrors,
          resetAndClearErrors,
          defaults,
          isDirty,
          reset,
          submit,
      })}
          <input type="text" name="name" />

          {#if errors.name}
              <div>{errors.name}</div>
          {/if}

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

          {#if wasSuccessful}
              <div>User created successfully!</div>
          {/if}
      {/snippet}
  </Form>
  ```
</CodeGroup>

Method `defaults` cho phép cập nhật giá trị mặc định của form theo giá trị field hiện tại. Sau khi gọi, các lần `reset()` tiếp theo sẽ khôi phục field về các mặc định mới này và property `isDirty` sẽ theo dõi thay đổi so với chúng. Khác với `useForm`, method này không nhận đối số và luôn dùng toàn bộ giá trị hiện tại của form.

Object `errors` dùng dotted notation cho field lồng nhau, cho phép hiển thị validation message cho cấu trúc form phức tạp.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <Form action="/users" method="post" #default="{ errors }">
      <input type="text" name="user.name" />
      <div v-if="errors['user.name']">{{ errors['user.name'] }}</div>
  </Form>
  ```

  ```jsx React icon="react" theme={null}
  <Form action="/users" method="post">
      {({ errors }) => (
          <>
              <input type="text" name="user.name" />
              {errors['user.name'] && <div>{errors['user.name']}</div>}
          </>
      )}
  </Form>
  ```

  ```svelte Svelte 4 icon="s" theme={null}
  <Form action="/users" method="post" let:errors>
      <input type="text" name="user.name" />
      {#if errors['user.name']}
          <div>{errors['user.name']}</div>
      {/if}
  </Form>
  ```

  ```svelte Svelte 5 icon="s" theme={null}
  <Form action="/users" method="post">
      {#snippet children({ errors })}
          <input type="text" name="user.name" />
          {#if errors['user.name']}
              <div>{errors['user.name']}</div>
          {/if}
      {/snippet}
  </Form>
  ```
</CodeGroup>

### Props và options

Ngoài `action` và `method`, component `<Form>` nhận nhiều prop khác. Nhiều prop giống hệt các tùy chọn có trong [visit options](/v2/the-basics/manual-visits) của Inertia.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <template>
      <Form
          action="/profile"
          method="put"
          error-bag="profile"
          query-string-array-format="indices"
          :headers="{ 'X-Custom-Header': 'value' }"
          :show-progress="false"
          :transform="data => ({ ...data, timestamp: Date.now() })"
          :invalidate-cache-tags="['users', 'dashboard']"
          disable-while-processing
          :options="{
              preserveScroll: true,
              preserveState: true,
              preserveUrl: true,
              replace: true,
              only: ['users', 'flash'],
              except: ['secret'],
              reset: ['page'],
          }"
      >
          <input type="text" name="name" />
          <button type="submit">Update</button>
      </Form>
  </template>
  ```

  ```jsx React icon="react" theme={null}
  <Form
      action="/profile"
      method="put"
      errorBag="profile"
      queryStringArrayFormat="indices"
      headers={{ 'X-Custom-Header': 'value' }}
      showProgress={false}
      transform={data => ({ ...data, timestamp: Date.now() })}
      invalidateCacheTags={['users', 'dashboard']}
      disableWhileProcessing
      options={{
          preserveScroll: true,
          preserveState: true,
          preserveUrl: true,
          replace: true,
          only: ['users', 'flash'],
          except: ['secret'],
          reset: ['page'],
      }}
  >
      <input type="text" name="name" />
      <button type="submit">Update</button>
  </Form>
  ```

  ```svelte Svelte icon="s" theme={null}
  <Form
      action="/profile"
      method="put"
      errorBag="profile"
      queryStringArrayFormat="indices"
      headers={{ 'X-Custom-Header': 'value' }}
      showProgress={false}
      transform={data => ({ ...data, timestamp: Date.now() })}
      invalidateCacheTags={['users', 'dashboard']}
      disableWhileProcessing
      options={{
          preserveScroll: true,
          preserveState: true,
          preserveUrl: true,
          replace: true,
          only: ['users', 'flash'],
          except: ['secret'],
          reset: ['page'],
      }}
  >
      <input type="text" name="name" />
      <button type="submit">Update</button>
  </Form>
  ```
</CodeGroup>

Một số prop được chủ ý nhóm dưới `options` thay vì đặt ở top-level để tránh nhầm lẫn. Ví dụ, `only`, `except` và `reset` liên quan đến *partial reload*, không phải *partial submission*. Quy tắc chung: prop top-level dành cho chính quá trình submit form, còn `options` kiểm soát cách Inertia xử lý visit tiếp theo.

<ClientSpecific>
  Khi đặt prop <ReactSpecific>`disableWhileProcessing`</ReactSpecific><SvelteSpecific>`disableWhileProcessing`</SvelteSpecific><VueSpecific>`disable-while-processing`</VueSpecific>, component `Form` sẽ thêm attribute `inert` vào thẻ HTML `form` trong lúc form đang xử lý để ngăn người dùng tương tác.
</ClientSpecific>

Để style form trong lúc đang xử lý, bạn có thể target form inert theo các cách sau.

<CodeGroup>
  ```jsx Tailwind 4 theme={null}
  <Form
      action="/profile"
      method="put"
      disableWhileProcessing
      className="inert:opacity-50 inert:pointer-events-none"
  >
      {/* Your form fields here */}
  </Form>
  ```

  ```css CSS theme={null}
  form[inert] {
      opacity: 0.5;
      pointer-events: none;
  }
  ```
</CodeGroup>

### Sự kiện

Component `<Form>` phát đầy đủ các [event](/v2/advanced/events) visit tiêu chuẩn khi submit form.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <template>
      <Form
          action="/users"
          method="post"
          @before="handleBefore"
          @start="handleStart"
          @progress="handleProgress"
          @success="handleSuccess"
          @error="handleError"
          @finish="handleFinish"
          @cancel="handleCancel"
          @cancelToken="handleCancelToken"
      >
          <input type="text" name="name" />
          <button type="submit">Create User</button>
      </Form>
  </template>
  ```

  ```jsx React icon="react" theme={null}
  <Form
      action="/users"
      method="post"
      onCancelToken={handleCancelToken}
      onBefore={handleBefore}
      onStart={handleStart}
      onProgress={handleProgress}
      onCancel={handleCancel}
      onSuccess={handleSuccess}
      onError={handleError}
      onFinish={handleFinish}
  >
      <input type="text" name="name" />
      <button type="submit">Create User</button>
  </Form>
  ```

  ```svelte Svelte 4 icon="s" theme={null}
  <Form
      action="/users"
      method="post"
      on:cancelToken={handleCancelToken}
      on:before={handleBefore}
      on:start={handleStart}
      on:progress={handleProgress}
      on:cancel={handleCancel}
      on:success={handleSuccess}
      on:error={handleError}
      on:finish={handleFinish}
  >
      <input type="text" name="name" />
      <button type="submit">Create User</button>
  </Form>
  ```

  ```svelte Svelte 5 icon="s" theme={null}
  <Form
      action="/users"
      method="post"
      onCancelToken={handleCancelToken}
      onBefore={handleBefore}
      onStart={handleStart}
      onProgress={handleProgress}
      onCancel={handleCancel}
      onSuccess={handleSuccess}
      onError={handleError}
      onFinish={handleFinish}
  >
      <input type="text" name="name" />
      <button type="submit">Create User</button>
  </Form>
  ```
</CodeGroup>

### Reset form

Component `Form` cung cấp một số attribute cho phép reset form sau khi submit.

Có thể dùng `resetOnSuccess` để reset form sau khi submit thành công.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <template>
      <!-- Reset the entire form on success -->
      <Form action="/users" method="post" resetOnSuccess>
          <input type="text" name="name" />
          <input type="email" name="email" />
          <button type="submit">Submit</button>
      </Form>

      <!-- Reset specific fields on success -->
      <Form action="/users" method="post" :resetOnSuccess="['name']">
          <input type="text" name="name" />
          <input type="email" name="email" />
          <button type="submit">Submit</button>
      </Form>
  </template>
  ```

  ```jsx React icon="react" theme={null}
  // Reset the entire form on success
  <Form action="/users" method="post" resetOnSuccess>
      <input type="text" name="name" />
      <input type="email" name="email" />
      <button type="submit">Submit</button>
  </Form>

  // Reset specific fields on success
  <Form action="/users" method="post" resetOnSuccess={['name']}>
      <input type="text" name="name" />
      <input type="email" name="email" />
      <button type="submit">Submit</button>
  </Form>
  ```

  ```svelte Svelte icon="s" theme={null}
  <!-- Reset the entire form on success -->
  <Form action="/users" method="post" resetOnSuccess>
      <input type="text" name="name" />
      <input type="email" name="email" />
      <button type="submit">Submit</button>
  </Form>

  <!-- Reset specific fields on success -->
  <Form action="/users" method="post" resetOnSuccess={['name']}>
      <input type="text" name="name" />
      <input type="email" name="email" />
      <button type="submit">Submit</button>
  </Form>
  ```
</CodeGroup>

Có thể dùng `resetOnError` để reset form sau khi xảy ra lỗi.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <template>
      <!-- Reset the entire form on success -->
      <Form action="/users" method="post" resetOnError>
          <input type="text" name="name" />
          <input type="email" name="email" />
          <button type="submit">Submit</button>
      </Form>

      <!-- Reset specific fields on success -->
      <Form action="/users" method="post" :resetOnError="['name']">
          <input type="text" name="name" />
          <input type="email" name="email" />
          <button type="submit">Submit</button>
      </Form>
  </template>
  ```

  ```jsx React icon="react" theme={null}
  // Reset the entire form on success
  <Form action="/users" method="post" resetOnError>
      <input type="text" name="name" />
      <input type="email" name="email" />
      <button type="submit">Submit</button>
  </Form>

  // Reset specific fields on success
  <Form action="/users" method="post" resetOnError={['name']}>
      <input type="text" name="name" />
      <input type="email" name="email" />
      <button type="submit">Submit</button>
  </Form>
  ```

  ```svelte Svelte icon="s" theme={null}
  <!-- Reset the entire form on success -->
  <Form action="/users" method="post" resetOnError>
      <input type="text" name="name" />
      <input type="email" name="email" />
      <button type="submit">Submit</button>
  </Form>

  <!-- Reset specific fields on success -->
  <Form action="/users" method="post" resetOnError={['name']}>
      <input type="text" name="name" />
      <input type="email" name="email" />
      <button type="submit">Submit</button>
  </Form>
  ```
</CodeGroup>

### Thiết lập giá trị mặc định mới

Component `Form` cung cấp attribute `setDefaultsOnSuccess` để đặt các giá trị hiện tại của form làm mặc định mới sau khi submit thành công.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <template>
      <Form action="/users" method="post" setDefaultsOnSuccess>
          <input type="text" name="name" />
          <input type="email" name="email" />
          <button type="submit">Submit</button>
      </Form>
  </template>
  ```

  ```jsx React icon="react" theme={null}
  <Form action="/users" method="post" setDefaultsOnSuccess>
      <input type="text" name="name" />
      <input type="email" name="email" />
      <button type="submit">Submit</button>
  </Form>
  ```

  ```svelte Svelte icon="s" theme={null}
  <Form action="/users" method="post" setDefaultsOnSuccess>
      <input type="text" name="name" />
      <input type="email" name="email" />
      <button type="submit">Submit</button>
  </Form>
  ```
</CodeGroup>

### Dotted key notation

Component `<Form>` hỗ trợ dotted key notation để tạo object lồng nhau từ tên input phẳng. Đây là cách thuận tiện để tổ chức dữ liệu form.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <template>
      <Form action="/users" method="post">
          <input type="text" name="user.name" />
          <input type="text" name="user.skills[]" />
          <input type="text" name="address.street" />
          <button type="submit">Submit</button>
      </Form>
  </template>
  ```

  ```jsx React icon="react" theme={null}
  <Form action="/users" method="post">
      <input type="text" name="user.name" />
      <input type="text" name="user.skills[]" />
      <input type="text" name="address.street" />
      <button type="submit">Submit</button>
  </Form>
  ```

  ```svelte Svelte icon="s" theme={null}
  <Form action="/users" method="post">
      <input type="text" name="user.name" />
      <input type="text" name="user.skills[]" />
      <input type="text" name="address.street" />
      <button type="submit">Submit</button>
  </Form>
  ```
</CodeGroup>

Ví dụ trên sẽ tạo ra cấu trúc dữ liệu sau.

```json theme={null}
{
    "user": {
        "name": "John Doe",
        "skills": ["JavaScript"]
    },
    "address": {
        "street": "123 Main St"
    }
}
```

Nếu cần dấu chấm literal trong tên field (không dùng làm dấu phân tách object lồng nhau), bạn có thể escape bằng dấu gạch chéo ngược.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <template>
      <Form action="/config" method="post">
          <input type="text" name="app\.name" />
          <input type="text" name="settings.theme\.mode" />
          <button type="submit">Save</button>
      </Form>
  </template>
  ```

  ```jsx React icon="react" theme={null}
  <Form action="/config" method="post">
      <input type="text" name="app\.name" />
      <input type="text" name="settings.theme\.mode" />
      <button type="submit">Save</button>
  </Form>
  ```

  ```svelte Svelte icon="s" theme={null}
  <Form action="/config" method="post">
      <input type="text" name="app\.name" />
      <input type="text" name="settings.theme\.mode" />
      <button type="submit">Save</button>
  </Form>
  ```
</CodeGroup>

Ví dụ trên sẽ tạo ra cấu trúc dữ liệu sau.

```json theme={null}
{
    "app.name": "My Application",
    "settings": {
        "theme.mode": "dark"
    }
}
```

### Truy cập bằng code

Bạn có thể truy cập các method của form bằng code thông qua ref. Đây là lựa chọn thay thế cho [slot props](#slot-props) khi cần kích hoạt action của form từ bên ngoài form.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <script setup>
  import { ref } from 'vue'
  import { Form } from '@inertiajs/vue3'

  const formRef = ref()

  const handleSubmit = () => {
      formRef.value.submit()
  }
  </script>

  <template>
      <Form ref="formRef" action="/users" method="post">
          <input type="text" name="name" />
          <button type="submit">Submit</button>
      </Form>

      <button @click="handleSubmit">Submit Programmatically</button>
  </template>
  ```

  ```jsx React icon="react" theme={null}
  import { useRef } from 'react'
  import { Form } from '@inertiajs/react'

  export default function CreateUser() {
      const formRef = useRef()

      const handleSubmit = () => {
          formRef.current.submit()
      }

      return (
          <Form ref={formRef} action="/users" method="post">
              <input type="text" name="name" />
              <button type="submit">Submit</button>
          </Form>

          <button onClick={handleSubmit}>Submit Programmatically</button>
      )
  }
  ```

  ```svelte Svelte icon="s" theme={null}
  <script>
  import { Form } from '@inertiajs/svelte'

  let formRef

  function handleSubmit() {
      formRef.submit()
  }
  </script>

  <Form bind:this={formRef} action="/users" method="post">
      <input type="text" name="name" />
      <button type="submit">Submit</button>
  </Form>

  <button on:click={handleSubmit}>Submit Programmatically</button>
  ```
</CodeGroup>

Trong React và Vue, ref cho phép truy cập toàn bộ method và reactive state của form. Trong Svelte, ref chỉ expose method, vì vậy reactive state như `isDirty` và `errors` nên được truy cập qua [slot props](#slot-props).

### Form context

<Badge>v2.3.9+</Badge>

Đôi khi bạn muốn truy cập state hoặc method của form từ các child component lồng sâu mà không phải truyền prop qua nhiều tầng. Hook `useFormContext` cho phép truy cập state và method của component `<Form>` cha từ bất kỳ child component nào.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <script setup>
  import { useFormContext } from '@inertiajs/vue3'

  const form = useFormContext()
  </script>

  <template>
      <div v-if="form">
          <span v-if="form.isDirty">Unsaved changes</span>
          <span v-if="form.errors.name">{{ form.errors.name }}</span>
          <button type="button" @click="form.submit()">Submit</button>
          <button type="button" @click="form.reset()">Reset</button>
      </div>
  </template>
  ```

  ```jsx React icon="react" theme={null}
  import { useFormContext } from '@inertiajs/react'

  export default function FormActions() {
      const form = useFormContext()

      if (!form) {
          return null
      }

      return (
          <div>
              {form.isDirty && <span>Unsaved changes</span>}
              {form.errors.name && <span>{form.errors.name}</span>}
              <button type="button" onClick={() => form.submit()}>Submit</button>
              <button type="button" onClick={() => form.reset()}>Reset</button>
          </div>
      )
  }
  ```

  ```svelte Svelte icon="s" theme={null}
  <script>
  import { useFormContext } from '@inertiajs/svelte'

  const form = useFormContext()
  </script>

  {#if $form}
      {#if $form.isDirty}<span>Unsaved changes</span>{/if}
      {#if $form.errors.name}<span>{$form.errors.name}</span>{/if}
      <button type="button" on:click={() => $form.submit()}>Submit</button>
      <button type="button" on:click={() => $form.reset()}>Reset</button>
  {/if}
  ```
</CodeGroup>

Context cung cấp đầy đủ property và method giống như [slot props](#slot-props).

### Precognition

<Badge>v2.3+</Badge>

Component `<Form>` tích hợp sẵn hỗ trợ [Laravel Precognition](https://laravel.com/docs/precognition), cho phép validation form theo thời gian thực mà không cần sao chép validation rule phía máy chủ sang client.

<Note>
  Precognition yêu cầu hỗ trợ phía máy chủ. Người dùng Laravel nên xem [tài liệu Laravel Precognition](https://laravel.com/docs/precognition) để biết cách thiết lập. Với framework khác, xem [trang giao thức](/v2/core-concepts/the-protocol#request-headers) để biết chi tiết triển khai.
</Note>

Sau khi cấu hình máy chủ, gọi `validate()` với tên field để kích hoạt validation cho field đó. Helper `invalid()` kiểm tra field có lỗi validation hay không, còn `validating` cho biết request đang được xử lý.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <template>
      <Form action="/users" method="post" #default="{ errors, invalid, validate, validating }">
          <label for="name">Name:</label>
          <input id="name" name="name" @change="validate('name')" />
          <p v-if="invalid('name')">{{ errors.name }}</p>

          <label for="email">Email:</label>
          <input id="email" name="email" @change="validate('email')" />
          <p v-if="invalid('email')">{{ errors.email }}</p>

          <p v-if="validating">Validating...</p>

          <button type="submit">Create User</button>
      </Form>
  </template>
  ```

  ```jsx React icon="react" theme={null}
  <Form action="/users" method="post">
      {({ errors, invalid, validate, validating }) => (
          <>
              <label htmlFor="name">Name:</label>
              <input id="name" name="name" onChange={() => validate('name')} />
              {invalid('name') && <p>{errors.name}</p>}

              <label htmlFor="email">Email:</label>
              <input id="email" name="email" onChange={() => validate('email')} />
              {invalid('email') && <p>{errors.email}</p>}

              {validating && <p>Validating...</p>}

              <button type="submit">Create User</button>
          </>
      )}
  </Form>
  ```

  ```svelte Svelte 4 icon="s" theme={null}
  <Form
      action="/users"
      method="post"
      let:errors
      let:invalid
      let:validate
      let:validating
  >
      <label for="name">Name:</label>
      <input id="name" name="name" on:change={() => validate('name')} />
      {#if invalid('name')}
          <p>{errors.name}</p>
      {/if}

      <label for="email">Email:</label>
      <input id="email" name="email" on:change={() => validate('email')} />
      {#if invalid('email')}
          <p>{errors.email}</p>
      {/if}

      {#if validating}
          <p>Validating...</p>
      {/if}

      <button type="submit">Create User</button>
  </Form>
  ```

  ```svelte Svelte 5 icon="s" theme={null}
  <Form action="/users" method="post">
      {#snippet children({ errors, invalid, validate, validating })}
          <label for="name">Name:</label>
          <input id="name" name="name" onchange={() => validate('name')} />
          {#if invalid('name')}
              <p>{errors.name}</p>
          {/if}

          <label for="email">Email:</label>
          <input id="email" name="email" onchange={() => validate('email')} />
          {#if invalid('email')}
              <p>{errors.email}</p>
          {/if}

          {#if validating}
              <p>Validating...</p>
          {/if}

          <button type="submit">Create User</button>
      {/snippet}
  </Form>
  ```
</CodeGroup>

Bạn cũng có thể dùng helper `valid()` để kiểm tra field đã pass validation hay chưa.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <Form action="/users" method="post" #default="{ errors, invalid, valid, validate }">
      <input name="email" @change="validate('email')" />
      <p v-if="valid('email')">Valid email address</p>
      <p v-if="invalid('email')">{{ errors.email }}</p>
  </Form>
  ```

  ```jsx React icon="react" theme={null}
  <Form action="/users" method="post">
      {({ errors, invalid, valid, validate }) => (
          <>
              <input name="email" onChange={() => validate('email')} />
              {valid('email') && <p>Valid email address</p>}
              {invalid('email') && <p>{errors.email}</p>}
          </>
      )}
  </Form>
  ```

  ```svelte Svelte 4 icon="s" theme={null}
  <Form action="/users" method="post" let:errors let:invalid let:valid let:validate>
      <input name="email" on:change={() => validate('email')} />
      {#if valid('email')}
          <p>Valid email address</p>
      {/if}
      {#if invalid('email')}
          <p>{errors.email}</p>
      {/if}
  </Form>
  ```

  ```svelte Svelte 5 icon="s" theme={null}
  <Form action="/users" method="post">
      {#snippet children({ errors, invalid, valid, validate })}
          <input name="email" onchange={() => validate('email')} />
          {#if valid('email')}
              <p>Valid email address</p>
          {/if}
          {#if invalid('email')}
              <p>{errors.email}</p>
          {/if}
      {/snippet}
  </Form>
  ```
</CodeGroup>

<Warning>
  Một input form chỉ được xem là valid hoặc invalid sau khi nó đã thay đổi và response validation đã được nhận.
</Warning>

#### Validation nhiều field

Bạn có thể validation nhiều field cùng lúc bằng tùy chọn `only`. Điều này đặc biệt hữu ích khi xây dựng form dạng wizard, nơi bạn muốn validation toàn bộ field đang hiển thị trước khi sang bước tiếp theo.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <Form action="/users" method="post" #default="{ validate }">
      <!-- Step 1 fields -->
      <input name="name" />
      <input name="email" />

      <button
          type="button"
          @click="validate({
              only: ['name', 'email'],
              onSuccess: () => goToNextStep(),
              onValidationError: () => showErrors(),
          })"
      >
          Next Step
      </button>
  </Form>
  ```

  ```jsx React icon="react" theme={null}
  <Form action="/users" method="post">
      {({ validate }) => (
          <>
              {/* Step 1 fields */}
              <input name="name" />
              <input name="email" />

              <button
                  type="button"
                  onClick={() => validate({
                      only: ['name', 'email'],
                      onSuccess: () => goToNextStep(),
                      onValidationError: () => showErrors(),
                  })}
              >
                  Next Step
              </button>
          </>
      )}
  </Form>
  ```

  ```svelte Svelte 4 icon="s" theme={null}
  <Form action="/users" method="post" let:validate>
      <!-- Step 1 fields -->
      <input name="name" />
      <input name="email" />

      <button
          type="button"
          on:click={() => validate({
              only: ['name', 'email'],
              onSuccess: () => goToNextStep(),
              onValidationError: () => showErrors(),
          })}
      >
          Next Step
      </button>
  </Form>
  ```

  ```svelte Svelte 5 icon="s" theme={null}
  <Form action="/users" method="post">
      {#snippet children({ validate })}
          <!-- Step 1 fields -->
          <input name="name" />
          <input name="email" />

          <button
              type="button"
              onclick={() => validate({
                  only: ['name', 'email'],
                  onSuccess: () => goToNextStep(),
                  onValidationError: () => showErrors(),
              })}
          >
              Next Step
          </button>
      {/snippet}
  </Form>
  ```
</CodeGroup>

#### Touch và validate

Method `touch()` đánh dấu các field là "touched" mà không kích hoạt validation. Sau đó bạn có thể validation tất cả field đã touched bằng cách gọi `validate()` không có đối số.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <Form action="/users" method="post" #default="{ validate, touch, touched }">
      <input name="name" @blur="touch('name')" />
      <input name="email" @blur="touch('email')" />
      <input name="phone" @blur="touch('phone')" />

      <button type="button" @click="validate()">Validate Touched Fields</button>

      <p v-if="touched('name')">Name has been touched</p>
  </Form>
  ```

  ```jsx React icon="react" theme={null}
  <Form action="/users" method="post">
      {({ validate, touch, touched }) => (
          <>
              <input name="name" onBlur={() => touch('name')} />
              <input name="email" onBlur={() => touch('email')} />
              <input name="phone" onBlur={() => touch('phone')} />

              <button type="button" onClick={() => validate()}>Validate Touched Fields</button>

              {touched('name') && <p>Name has been touched</p>}
          </>
      )}
  </Form>
  ```

  ```svelte Svelte 4 icon="s" theme={null}
  <Form action="/users" method="post" let:validate let:touch let:touched>
      <input name="name" on:blur={() => touch('name')} />
      <input name="email" on:blur={() => touch('email')} />
      <input name="phone" on:blur={() => touch('phone')} />

      <button type="button" on:click={() => validate()}>Validate Touched Fields</button>

      {#if touched('name')}
          <p>Name has been touched</p>
      {/if}
  </Form>
  ```

  ```svelte Svelte 5 icon="s" theme={null}
  <Form action="/users" method="post">
      {#snippet children({ validate, touch, touched })}
          <input name="name" onblur={() => touch('name')} />
          <input name="email" onblur={() => touch('email')} />
          <input name="phone" onblur={() => touch('phone')} />

          <button type="button" onclick={() => validate()}>Validate Touched Fields</button>

          {#if touched('name')}
              <p>Name has been touched</p>
          {/if}
      {/snippet}
  </Form>
  ```
</CodeGroup>

Helper `touched()` cũng có thể được gọi không đối số để kiểm tra có field nào đã touched hay chưa. Method `reset()` xóa touched state của các field được reset.

#### Options

Method `validate()` nhận object options chứa callback và cấu hình.

```js theme={null}
validate('username', {
    onSuccess: () => {
        // Validation passed...
    },
    onValidationError: (response) => {
        // Validation failed (422 response)...
    },
    onBeforeValidation: (newRequest, oldRequest) => {
        // Return false to prevent validation...
    },
    onFinish: () => {
        // Always runs after validation...
    },
})
```

Bạn cũng có thể gọi `validate()` chỉ với object options để validation các field cụ thể.

```js theme={null}
validate({
    only: ['name', 'email'],
    onSuccess: () => goToNextStep(),
})
```

Request validation tự động được debounce. Request đầu tiên chạy ngay lập tức, các thay đổi tiếp theo được debounce (mặc định 1500ms). Bạn có thể tùy chỉnh timeout này.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <Form action="/users" method="post" :validation-timeout="500">
      <!-- ... -->
  </Form>
  ```

  ```jsx React icon="react" theme={null}
  <Form action="/users" method="post" validationTimeout={500}>
      {/* ... */}
  </Form>
  ```

  ```svelte Svelte icon="s" theme={null}
  <Form action="/users" method="post" validationTimeout={500}>
      <!-- ... -->
  </Form>
  ```
</CodeGroup>

Mặc định, file bị loại khỏi request validation để tránh upload không cần thiết. Bạn có thể bật validation file khi cần kiểm tra input file như kích thước hoặc MIME type.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <Form action="/users" method="post" validate-files>
      <!-- ... -->
  </Form>
  ```

  ```jsx React icon="react" theme={null}
  <Form action="/users" method="post" validateFiles>
      {/* ... */}
  </Form>
  ```

  ```svelte Svelte icon="s" theme={null}
  <Form action="/users" method="post" validateFiles>
      <!-- ... -->
  </Form>
  ```
</CodeGroup>

Mặc định, lỗi validation được đơn giản hóa thành chuỗi (message lỗi đầu tiên). Bạn có thể giữ lỗi dưới dạng array để hiển thị toàn bộ message cho field có nhiều validation rule.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <Form action="/users" method="post" with-all-errors>
      <!-- ... -->
  </Form>
  ```

  ```jsx React icon="react" theme={null}
  <Form action="/users" method="post" withAllErrors>
      {/* ... */}
  </Form>
  ```

  ```svelte Svelte icon="s" theme={null}
  <Form action="/users" method="post" withAllErrors>
      <!-- ... -->
  </Form>
  ```
</CodeGroup>

## Form helper

Ngoài component `<Form>`, Inertia còn cung cấp helper `useForm` khi bạn cần kiểm soát bằng code đối với dữ liệu form và hành vi submit.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <script setup>
  import { useForm } from '@inertiajs/vue3'

  const form = useForm({
      email: null,
      password: null,
      remember: false,
  })
  </script>

  <template>
      <form @submit.prevent="form.post('/login')">
          <input type="text" v-model="form.email">
          <div v-if="form.errors.email">{{ form.errors.email }}</div>
          <input type="password" v-model="form.password">
          <div v-if="form.errors.password">{{ form.errors.password }}</div>
          <input type="checkbox" v-model="form.remember"> Remember Me
          <button type="submit" :disabled="form.processing">Login</button>
      </form>
  </template>
  ```

  ```jsx React icon="react" theme={null}
  import { useForm } from '@inertiajs/react'

  const { data, setData, post, processing, errors } = useForm({
      email: '',
      password: '',
      remember: false,
  })

  function submit(e) {
      e.preventDefault()
      post('/login')
  }

  return (
      <form onSubmit={submit}>
          <input type="text" value={data.email} onChange={e => setData('email', e.target.value)} />
          {errors.email && <div>{errors.email}</div>}
          <input type="password" value={data.password} onChange={e => setData('password', e.target.value)} />
          {errors.password && <div>{errors.password}</div>}
          <input type="checkbox" checked={data.remember} onChange={e => setData('remember', e.target.checked)} /> Remember Me
          <button type="submit" disabled={processing}>Login</button>
      </form>
  )
  ```

  ```svelte Svelte 4 icon="s" theme={null}
  <script>
  import { useForm } from '@inertiajs/svelte'

  const form = useForm({
      email: null,
      password: null,
      remember: false,
  })

  function submit() {
      $form.post('/login')
  }
  </script>

  <form on:submit|preventDefault={submit}>
      <input type="text" bind:value={$form.email} />
      {#if $form.errors.email}
          <div class="form-error">{$form.errors.email}</div>
      {/if}
      <input type="password" bind:value={$form.password} />
      {#if $form.errors.password}
          <div class="form-error">{$form.errors.password}</div>
      {/if}
      <input type="checkbox" bind:checked={$form.remember} /> Remember Me
      <button type="submit" disabled={$form.processing}>Submit</button>
  </form>
  ```

  ```svelte Svelte 5 icon="s" theme={null}
  <script>
  import { useForm } from '@inertiajs/svelte'

  const form = useForm({
      email: null,
      password: null,
      remember: false,
  })

  function submit(e) {
      e.preventDefault()
      $form.post('/login')
  }
  </script>

  <form onsubmit={submit}>
      <input type="text" bind:value={$form.email} />
      {#if $form.errors.email}
          <div class="form-error">{$form.errors.email}</div>
      {/if}
      <input type="password" bind:value={$form.password} />
      {#if $form.errors.password}
          <div class="form-error">{$form.errors.password}</div>
      {/if}
      <input type="checkbox" bind:checked={$form.remember} /> Remember Me
      <button type="submit" disabled={$form.processing}>Submit</button>
  </form>
  ```
</CodeGroup>

Để submit form, bạn có thể dùng các phương thức `get`, `post`, `put`, `patch` và `delete`.

<CodeGroup>
  ```js Vue icon="vuejs" theme={null}
  form.submit(method, url, options)
  form.get(url, options)
  form.post(url, options)
  form.put(url, options)
  form.patch(url, options)
  form.delete(url, options)
  ```

  ```js React icon="react" theme={null}
  const { submit, get, post, put, patch, delete: destroy } = useForm({ ... })

  submit(method, url, options)
  get(url, options)
  post(url, options)
  put(url, options)
  patch(url, options)
  destroy(url, options)
  ```

  ```js Svelte icon="s" theme={null}
  $form.submit(method, url, options)
  $form.get(url, options)
  $form.post(url, options)
  $form.put(url, options)
  $form.patch(url, options)
  $form.delete(url, options)
  ```
</CodeGroup>

Các method submit hỗ trợ mọi [visit option](/v2/the-basics/manual-visits) thông thường như `preserveState`, `preserveScroll` và event callback, hữu ích để thực hiện tác vụ sau khi submit thành công. Ví dụ, bạn có thể dùng callback `onSuccess` để reset input về trạng thái ban đầu.

<CodeGroup>
  ```js Vue icon="vuejs" theme={null}
  form.post('/profile', {
      preserveScroll: true,
      onSuccess: () => form.reset('password'),
  })
  ```

  ```js React icon="react" theme={null}
  const { post, reset } = useForm({ ... })

  post('/profile', {
      preserveScroll: true,
      onSuccess: () => reset('password'),
  })
  ```

  ```js Svelte icon="s" theme={null}
  $form.post('/profile', {
      preserveScroll: true,
      onSuccess: () => $form.reset('password'),
  })
  ```
</CodeGroup>

Bạn có thể truyền HTTP method và URL làm hai đối số đầu tiên cho `useForm()`, sau đó gọi `submit()` không đối số để gửi request. Cách này cũng mở khóa validation theo thời gian thực. Xem [Precognition](#precognition-2) để biết chi tiết.

Nếu cần thay đổi dữ liệu form trước khi gửi đến máy chủ, bạn có thể thực hiện thông qua phương thức `transform()`.

<CodeGroup>
  ```js Vue icon="vuejs" theme={null}
  form
      .transform((data) => ({
          ...data,
          remember: data.remember ? 'on' : '',
      }))
      .post('/login')
  ```

  ```js React icon="react" theme={null}
  const { transform } = useForm({ ... })

  transform((data) => ({
      ...data,
      remember: data.remember ? 'on' : '',
  }))
  ```

  ```js Svelte icon="s" theme={null}
  $form
      .transform((data) => ({
          ...data,
          remember: data.remember ? 'on' : '',
      }))
      .post('/login')
  ```
</CodeGroup>

Bạn có thể dùng thuộc tính `processing` để theo dõi form có đang được submit hay không. Điều này hữu ích để ngăn submit hai lần bằng cách disable nút submit.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <button type="submit" :disabled="form.processing">Submit</button>
  ```

  ```jsx React icon="react" theme={null}
  const { processing } = useForm({ ... })

  <button type="submit" disabled={processing}>Submit</button>
  ```

  ```svelte Svelte icon="s" theme={null}
  <button type="submit" disabled={$form.processing}>Submit</button>
  ```
</CodeGroup>

Nếu form đang tải file lên, sự kiện tiến trình hiện tại có sẵn thông qua thuộc tính `progress`, giúp bạn dễ dàng hiển thị tiến độ tải lên.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <progress v-if="form.progress" :value="form.progress.percentage" max="100">
      {{ form.progress.percentage }}%
  </progress>
  ```

  ```jsx React icon="react" theme={null}
  const { progress } = useForm({ ... })

  {progress && (
      <progress value={progress.percentage} max="100">
          {progress.percentage}%
      </progress>
  )}
  ```

  ```svelte Svelte icon="s" theme={null}
  {#if $form.progress}
      <progress value={$form.progress.percentage} max="100">
          {$form.progress.percentage}%
      </progress>
  {/if}
  ```
</CodeGroup>

### Lỗi form

Nếu có lỗi validation của form, chúng có sẵn thông qua thuộc tính `errors`. Khi xây dựng ứng dụng Inertia dùng Laravel, lỗi form sẽ tự động được điền khi ứng dụng ném ra instance `ValidationException`, chẳng hạn khi dùng `{'$request->validate()'}`.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <div v-if="form.errors.email">{{ form.errors.email }}</div>
  ```

  ```jsx React icon="react" theme={null}
  const { errors } = useForm({ ... })

  {errors.email && <div>{errors.email}</div>}
  ```

  ```svelte Svelte icon="s" theme={null}
  {#if $form.errors.email}
      <div>{$form.errors.email}</div>
  {/if}
  ```
</CodeGroup>

Để tìm hiểu kỹ hơn về validation và lỗi form, hãy xem [tài liệu validation](/v2/the-basics/validation).

Để xác định form có lỗi hay không, bạn có thể dùng thuộc tính `hasErrors`. Để xóa lỗi form, dùng phương thức `clearErrors()`.

<CodeGroup>
  ```js Vue icon="vuejs" theme={null}
  // Clear all errors...
  form.clearErrors()

  // Clear errors for specific fields...
  form.clearErrors('field', 'anotherfield')
  ```

  ```js React icon="react" theme={null}
  const { clearErrors } = useForm({ ... })

  // Clear all errors...
  clearErrors()

  // Clear errors for specific fields...
  clearErrors('field', 'anotherfield')
  ```

  ```js Svelte icon="s" theme={null}
  // Clear all errors...
  $form.clearErrors()

  // Clear errors for specific fields...
  $form.clearErrors('field', 'anotherfield')
  ```
</CodeGroup>

Nếu dùng thư viện validation input phía client hoặc tự validation phía client, bạn có thể tự đặt lỗi cho form bằng method `setErrors()`.

<CodeGroup>
  ```js Vue icon="vuejs" theme={null}
  // Set a single error...
  form.setError('field', 'Your error message.');

  // Set multiple errors at once...
  form.setError({
      foo: 'Your error message for the foo field.',
      bar: 'Some other error for the bar field.'
  });
  ```

  ```js React icon="react" theme={null}
  const { setError } = useForm({ ... })

  // Set a single error...
  setError('field', 'Your error message.');

  // Set multiple errors at once...
  setError({
      foo: 'Your error message for the foo field.',
      bar: 'Some other error for the bar field.'
  });
  ```

  ```js Svelte icon="s" theme={null}
  // Set a single error
  $form.setError('field', 'Your error message.');

  // Set multiple errors at once
  $form.setError({
      foo: 'Your error message for the foo field.',
      bar: 'Some other error for the bar field.'
  });
  ```
</CodeGroup>

Khác với một lần submit form thực tế, props của trang không thay đổi khi bạn tự đặt lỗi trên một instance form.

Khi form được submit thành công, thuộc tính `wasSuccessful` sẽ là `true`. Ngoài ra, form còn có thuộc tính `recentlySuccessful`, được đặt thành `true` trong hai giây sau một lần submit thành công. Bạn có thể dùng thuộc tính này để hiển thị thông báo thành công tạm thời.

Bạn có thể tùy chỉnh thời lượng state `recentlySuccessful` bằng tùy chọn `form.recentlySuccessfulDuration` trong [giá trị mặc định của ứng dụng](/v2/installation/client-side-setup#configuring-defaults). Mặc định là `2000` mili giây.

### Reset form

Để reset các giá trị của form về giá trị mặc định, bạn có thể dùng phương thức `reset()`.

<CodeGroup>
  ```js Vue icon="vuejs" theme={null}
  // Reset the form...
  form.reset()

  // Reset specific fields...
  form.reset('field', 'anotherfield')
  ```

  ```js React icon="react" theme={null}
  const { reset } = useForm({ ... })

  // Reset the form...
  reset()

  // Reset specific fields...
  reset('field', 'anotherfield')
  ```

  ```js Svelte icon="s" theme={null}
  // Reset the form...
  $form.reset()

  // Reset specific fields...
  $form.reset('field', 'anotherfield')
  ```
</CodeGroup>

Đôi khi bạn muốn vừa khôi phục field về giá trị mặc định vừa xóa mọi lỗi validation. Thay vì gọi riêng `reset()` và `clearErrors()`, có thể dùng method `resetAndClearErrors()` để kết hợp cả hai hành động trong một lần gọi.

<CodeGroup>
  ```js Vue icon="vuejs" theme={null}
  // Reset the form and clear all errors...
  form.resetAndClearErrors()

  // Reset specific fields and clear their errors...
  form.resetAndClearErrors('field', 'anotherfield')
  ```

  ```js React icon="react" theme={null}
  const { resetAndClearErrors } = useForm({ ... })

  // Reset the form and clear all errors...
  resetAndClearErrors()

  // Reset specific fields and clear their errors...
  resetAndClearErrors('field', 'anotherfield')
  ```

  ```js Svelte icon="s" theme={null}
  // Reset the form and clear all errors...
  $form.resetAndClearErrors()

  // Reset specific fields and clear their errors...
  $form.resetAndClearErrors('field', 'anotherfield')
  ```
</CodeGroup>

### Thiết lập giá trị mặc định mới

Nếu các giá trị mặc định của form không còn phù hợp, bạn có thể dùng phương thức `defaults()` để cập nhật chúng. Sau đó, form sẽ được reset về đúng các giá trị này trong lần tiếp theo phương thức `reset()` được gọi.

<CodeGroup>
  ```js Vue icon="vuejs" theme={null}
  // Set the form's current values as the new defaults...
  form.defaults()

  // Update the default value of a single field...
  form.defaults('email', 'updated-default@example.com')

  // Update the default value of multiple fields...
  form.defaults({
      name: 'Updated Example',
      email: 'updated-default@example.com',
  })
  ```

  ```js React icon="react" theme={null}
  const { setDefaults } = useForm({ ... })

  // Set the form's current values as the new defaults...
  setDefaults()

  // Update the default value of a single field...
  setDefaults('email', 'updated-default@example.com')

  // Update the default value of multiple fields...
  setDefaults({
      name: 'Updated Example',
      email: 'updated-default@example.com',
  })
  ```

  ```js Svelte icon="s" theme={null}
  // Set the form's current values as the new defaults...
  $form.defaults()

  // Update the default value of a single field...
  $form.defaults('email', 'updated-default@example.com')

  // Change the default value of multiple fields...
  $form.defaults({
      name: 'Updated Example',
      email: 'updated-default@example.com',
  })
  ```
</CodeGroup>

### Theo dõi thay đổi field của form

Để xác định form có thay đổi nào hay không, bạn có thể dùng thuộc tính `isDirty`.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <div v-if="form.isDirty">There are unsaved form changes.</div>
  ```

  ```jsx React icon="react" theme={null}
  const { isDirty } = useForm({ ... })

  {isDirty && <div>There are unsaved form changes.</div>}
  ```

  ```svelte Svelte icon="s" theme={null}
  {#if $form.isDirty}
      <div>There are unsaved form changes.</div>
  {/if}
  ```
</CodeGroup>

### Hủy submit form

Để hủy một lần submit form, dùng phương thức `cancel()`.

<CodeGroup>
  ```js Vue icon="vuejs" theme={null}
  form.cancel()
  ```

  ```js React icon="react" theme={null}
  const { cancel } = useForm({ ... })

  cancel()
  ```

  ```js Svelte icon="s" theme={null}
  $form.cancel()
  ```
</CodeGroup>

### Dữ liệu form và history state

Để yêu cầu Inertia lưu dữ liệu và lỗi form vào [history state](/v2/data-props/remembering-state), bạn có thể cung cấp một form key duy nhất làm đối số đầu tiên khi khởi tạo form.

<CodeGroup>
  ```js Vue icon="vuejs" theme={null}
  import { useForm } from '@inertiajs/vue3'

  const form = useForm('CreateUser', data)
  const form = useForm(`EditUser:${user.id}\
  ```

  ```js React icon="react" theme={null}
  import { useForm } from '@inertiajs/react'

  const form = useForm('CreateUser', data)
  const form = useForm(`EditUser:${user.id}\
  ```

  ```js Svelte icon="s" theme={null}
  import { useForm } from '@inertiajs/svelte'

  const form = useForm('CreateUser', data)
  const form = useForm(`EditUser:${user.id}\
  ```
</CodeGroup>

#### Loại trừ field

Đôi khi bạn muốn ngăn một số field được lưu vào history state. Ví dụ, có thể bạn muốn loại password field vì lý do bảo mật.

<CodeGroup>
  ```js Vue icon="vuejs" theme={null}
  import { useForm } from '@inertiajs/vue3'

  const form = useForm('LoginForm', {
      email: '',
      password: '',
  }).dontRemember('password')
  ```

  ```js React icon="react" theme={null}
  import { useForm } from '@inertiajs/react'

  const form = useForm('LoginForm', {
      email: '',
      password: '',
  }).dontRemember('password')
  ```

  ```js Svelte icon="s" theme={null}
  import { useForm } from '@inertiajs/svelte'

  const form = useForm('LoginForm', {
      email: '',
      password: '',
  }).dontRemember('password')
  ```
</CodeGroup>

Có thể loại trừ nhiều field bằng cách truyền thêm đối số.

```js theme={null}
form.dontRemember('password', 'password_confirmation')
```

<Note>
  Một số trình duyệt kích hoạt prompt "lưu mật khẩu" mỗi khi giá trị password field được ghi vào history state, kể cả khi chưa submit form. Loại trừ password field giúp tránh vấn đề này.
</Note>

### Wayfinder

<Badge>v2.0.6+</Badge>

Khi dùng [Wayfinder](https://github.com/laravel/wayfinder) cùng form helper, bạn chỉ cần truyền object kết quả trực tiếp vào method `form.submit`. Form helper sẽ suy ra HTTP method và URL từ object Wayfinder.

<CodeGroup>
  ```js Vue icon="vuejs" theme={null}
  import { useForm } from '@inertiajs/vue3'
  import { store } from 'App/Http/Controllers/UserController'

  const form = useForm({
      name: 'John Doe',
      email: 'john.doe@example.com',
  })

  form.submit(store())
  ```

  ```js React icon="react" theme={null}
  import { useForm } from '@inertiajs/react'
  import { store } from 'App/Http/Controllers/UserController'

  const form = useForm({
      name: 'John Doe',
      email: 'john.doe@example.com',
  })

  form.submit(store())
  ```

  ```js Svelte icon="s" theme={null}
  import { useForm } from '@inertiajs/svelte'
  import { store } from 'App/Http/Controllers/UserController'

  const form = useForm({
      name: 'John Doe',
      email: 'john.doe@example.com',
  })

  form.submit(store())
  ```
</CodeGroup>

### Precognition

<Badge>v2.3+</Badge>

Tương tự component `<Form>`, helper `useForm` hỗ trợ [Precognition](#precognition) cho validation thời gian thực. Bạn có thể bật bằng cách chain method `withPrecognition()` với HTTP method và endpoint dành cho request validation.

<Note>
  Precognition yêu cầu hỗ trợ phía máy chủ. Người dùng Laravel nên xem [tài liệu Laravel Precognition](https://laravel.com/docs/precognition) để biết cách thiết lập. Với framework khác, xem [trang giao thức](/v2/core-concepts/the-protocol#request-headers) để biết chi tiết triển khai.
</Note>

<CodeGroup>
  ```js Vue icon="vuejs" theme={null}
  import { useForm } from '@inertiajs/vue3'

  const form = useForm({
      name: '',
      email: '',
  }).withPrecognition('post', '/users')
  ```

  ```js React icon="react" theme={null}
  import { useForm } from '@inertiajs/react'

  const form = useForm({
      name: '',
      email: '',
  }).withPrecognition('post', '/users')
  ```

  ```js Svelte icon="s" theme={null}
  import { useForm } from '@inertiajs/svelte'

  const form = useForm({
      name: '',
      email: '',
  }).withPrecognition('post', '/users')
  ```
</CodeGroup>

Để tương thích ngược với package `laravel-precognition`, bạn cũng có thể truyền method và URL làm các đối số đầu tiên cho `useForm()`.

```js theme={null}
const form = useForm('post', '/users', {
    name: '',
    email: '',
})
```

<Tip>
  Vì Precognition hiện đã được tích hợp sẵn, bạn có thể gỡ package `laravel-precognition` và import `useForm` trực tiếp từ adapter Inertia.
</Tip>

Bạn cũng có thể dùng [Wayfinder](https://github.com/laravel/wayfinder) khi bật Precognition.

```js theme={null}
import { store } from 'App/Http/Controllers/UserController'

const form = useForm({
    name: '',
    email: '',
}).withPrecognition(store())

// Or passing Wayfinder as the first argument...
const form = useForm(store(), {
    name: '',
    email: '',
})
```

Sau khi bật Precognition, gọi `validate()` với tên field để kích hoạt validation cho field đó. Helper `invalid()` kiểm tra field có lỗi validation hay không, còn `validating` cho biết request đang chạy.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <script setup>
  import { useForm } from '@inertiajs/vue3'

  const form = useForm('post', '/users', {
      name: '',
      email: '',
  })
  </script>

  <template>
      <form @submit.prevent="form.submit()">
          <input v-model="form.name" @change="form.validate('name')" />
          <p v-if="form.invalid('name')">{{ form.errors.name }}</p>

          <input v-model="form.email" @change="form.validate('email')" />
          <p v-if="form.invalid('email')">{{ form.errors.email }}</p>

          <p v-if="form.validating">Validating...</p>

          <button type="submit">Create User</button>
      </form>
  </template>
  ```

  ```jsx React icon="react" theme={null}
  import { useForm } from '@inertiajs/react'

  const { data, setData, submit, errors, validating, validate, invalid } = useForm('post', '/users', {
      name: '',
      email: '',
  })

  function handleSubmit(e) {
      e.preventDefault()
      submit()
  }

  return (
      <form onSubmit={handleSubmit}>
          <input value={data.name} onChange={e => setData('name', e.target.value)} onBlur={() => validate('name')} />
          {invalid('name') && <p>{errors.name}</p>}

          <input value={data.email} onChange={e => setData('email', e.target.value)} onBlur={() => validate('email')} />
          {invalid('email') && <p>{errors.email}</p>}

          {validating && <p>Validating...</p>}

          <button type="submit">Create User</button>
      </form>
  )
  ```

  ```svelte Svelte 4 icon="s" theme={null}
  <script>
  import { useForm } from '@inertiajs/svelte'

  const form = useForm('post', '/users', {
      name: '',
      email: '',
  })
  </script>

  <form on:submit|preventDefault={() => $form.submit()}>
      <input bind:value={$form.name} on:change={() => $form.validate('name')} />
      {#if $form.invalid('name')}
          <p>{$form.errors.name}</p>
      {/if}

      <input bind:value={$form.email} on:change={() => $form.validate('email')} />
      {#if $form.invalid('email')}
          <p>{$form.errors.email}</p>
      {/if}

      {#if $form.validating}
          <p>Validating...</p>
      {/if}

      <button type="submit">Create User</button>
  </form>
  ```

  ```svelte Svelte 5 icon="s" theme={null}
  <script>
  import { useForm } from '@inertiajs/svelte'

  const form = useForm('post', '/users', {
      name: '',
      email: '',
  })
  </script>

  <form onsubmit={(e) => { e.preventDefault(); $form.submit() }}>
      <input bind:value={$form.name} onchange={() => $form.validate('name')} />
      {#if $form.invalid('name')}
          <p>{$form.errors.name}</p>
      {/if}

      <input bind:value={$form.email} onchange={() => $form.validate('email')} />
      {#if $form.invalid('email')}
          <p>{$form.errors.email}</p>
      {/if}

      {#if $form.validating}
          <p>Validating...</p>
      {/if}

      <button type="submit">Create User</button>
  </form>
  ```
</CodeGroup>

Bạn cũng có thể dùng helper `valid()` để kiểm tra field đã pass validation hay chưa.

<Warning>
  Input form chỉ được xem valid hoặc invalid sau khi nó đã thay đổi và response validation được nhận. Gọi `validate('field')` sẽ không gửi request validation cho đến khi giá trị field khác dữ liệu ban đầu.
</Warning>

#### Touch và validate

Method `touch()` đánh dấu field là "touched" mà không kích hoạt validation. Sau đó bạn có thể validation toàn bộ field touched bằng cách gọi `validate()` không đối số. Helper `touched()` kiểm tra field đã touched hay chưa. Method `reset()` xóa touched state cho các field được reset.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <input v-model="form.name" @blur="form.touch('name')" />
  <input v-model="form.email" @blur="form.touch('email')" />

  <button type="button" @click="form.validate()">Validate Touched Fields</button>

  <p v-if="form.touched('name')">Name has been touched</p>
  ```

  ```jsx React icon="react" theme={null}
  <input value={data.name} onChange={e => setData('name', e.target.value)} onBlur={() => touch('name')} />
  <input value={data.email} onChange={e => setData('email', e.target.value)} onBlur={() => touch('email')} />

  <button type="button" onClick={() => validate()}>Validate Touched Fields</button>

  {touched('name') && <p>Name has been touched</p>}
  ```

  ```svelte Svelte icon="s" theme={null}
  <input bind:value={$form.name} on:blur={() => $form.touch('name')} />
  <input bind:value={$form.email} on:blur={() => $form.touch('email')} />

  <button type="button" on:click={() => $form.validate()}>Validate Touched Fields</button>

  {#if $form.touched('name')}
      <p>Name has been touched</p>
  {/if}
  ```
</CodeGroup>

#### Options

Request validation tự động được debounce. Request đầu tiên chạy ngay, các thay đổi tiếp theo được debounce (mặc định 1500ms). Bạn có thể tùy chỉnh timeout bằng `setValidationTimeout()`.

<CodeGroup>
  ```js Vue icon="vuejs" theme={null}
  const form = useForm('post', '/users', {
      name: '',
  }).setValidationTimeout(500)
  ```

  ```js React icon="react" theme={null}
  const form = useForm('post', '/users', {
      name: '',
  })

  form.setValidationTimeout(500)
  ```

  ```js Svelte icon="s" theme={null}
  const form = useForm('post', '/users', {
      name: '',
  })

  $form.setValidationTimeout(500)
  ```
</CodeGroup>

Mặc định, file bị loại khỏi request validation để tránh upload không cần thiết. Bạn có thể bật validation file bằng `validateFiles()`.

<CodeGroup>
  ```js Vue icon="vuejs" theme={null}
  const form = useForm('post', '/users', {
      avatar: null,
  }).validateFiles()
  ```

  ```js React icon="react" theme={null}
  const form = useForm('post', '/users', {
      avatar: null,
  })

  form.validateFiles()
  ```

  ```js Svelte icon="s" theme={null}
  const form = useForm('post', '/users', {
      avatar: null,
  })

  $form.validateFiles()
  ```
</CodeGroup>

Mặc định, lỗi validation được đơn giản hóa thành chuỗi (message lỗi đầu tiên). Bạn có thể yêu cầu nhận toàn bộ lỗi dưới dạng array bằng `withAllErrors()`.

<CodeGroup>
  ```js Vue icon="vuejs" theme={null}
  const form = useForm('post', '/users', {
      name: '',
  }).withAllErrors()
  ```

  ```js React icon="react" theme={null}
  const form = useForm('post', '/users', {
      name: '',
  })

  form.withAllErrors()
  ```

  ```js Svelte icon="s" theme={null}
  const form = useForm('post', '/users', {
      name: '',
  })

  $form.withAllErrors()
  ```
</CodeGroup>

Khi Precognition đã bật, bạn có thể gọi `submit()` không đối số để submit đến endpoint đã cấu hình.

## Response phía máy chủ

Khi dùng Inertia, thông thường bạn không kiểm tra response form ở phía client như với request XHR/fetch truyền thống. Thay vào đó, route hoặc controller phía máy chủ trả một response [redirect](/v2/the-basics/redirects) sau khi xử lý form, thường redirect đến trang thành công.

```php theme={null}
class UsersController extends Controller
{
    public function index()
    {
        return Inertia::render('Users/Index', [
            'users' => User::all(),
        ]);
    }

    public function store(Request $request)
    {
        User::create($request->validate([
            'first_name' => ['required', 'max:50'],
            'last_name' => ['required', 'max:50'],
            'email' => ['required', 'max:50', 'email'],
        ]));

        return to_route('users.index');
    }
}
```

Cách tiếp cận dựa trên redirect này hoạt động với mọi phương thức submit form: component `<Form>`, helper `useForm` và submit thủ công bằng router. Nó khiến việc xử lý form Inertia rất giống submit form truyền thống phía máy chủ.

## Validation phía máy chủ

Cả component `<Form>` và helper `useForm` đều tự động xử lý lỗi validation phía máy chủ. Khi máy chủ trả lỗi validation, chúng tự động có trong object `errors` mà không cần cấu hình thêm.

Khác với request XHR/fetch truyền thống nơi bạn có thể kiểm tra status code `422`, Inertia xử lý lỗi validation như một phần của flow dựa trên redirect, giống submit form truyền thống phía máy chủ nhưng không tải lại toàn bộ trang.

Để xem hướng dẫn đầy đủ về xử lý lỗi validation, bao gồm error bag và tình huống nâng cao, hãy xem [tài liệu validation](/v2/the-basics/validation).

## Submit form thủ công

Bạn cũng có thể submit form thủ công bằng trực tiếp các method `router` của Inertia mà không dùng component `<Form>` hoặc helper `useForm`:

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <script setup>
  import { reactive } from 'vue'
  import { router } from '@inertiajs/vue3'

  const form = reactive({
      first_name: null,
      last_name: null,
      email: null,
  })

  function submit() {
      router.post('/users', form)
  }
  </script>

  <template>
      <form @submit.prevent="submit">
          <label for="first_name">First name:</label>
          <input id="first_name" v-model="form.first_name" />
          <label for="last_name">Last name:</label>
          <input id="last_name" v-model="form.last_name" />
          <label for="email">Email:</label>
          <input id="email" v-model="form.email" />
          <button type="submit">Submit</button>
      </form>
  </template>
  ```

  ```jsx React icon="react" theme={null}
  import { useState } from 'react'
  import { router } from '@inertiajs/react'

  export default function Edit() {
      const [values, setValues] = useState({
          first_name: "",
          last_name: "",
          email: "",
      })

      function handleChange(e) {
          const key = e.target.id;
          const value = e.target.value
          setValues(values => ({
                  ...values,
                  [key]: value,
          }))
      }

      function handleSubmit(e) {
          e.preventDefault()
          router.post('/users', values)
      }

      return (
          <form onSubmit={handleSubmit}>
              <label htmlFor="first_name">First name:</label>
              <input id="first_name" value={values.first_name} onChange={handleChange} />
              <label htmlFor="last_name">Last name:</label>
              <input id="last_name" value={values.last_name} onChange={handleChange} />
              <label htmlFor="email">Email:</label>
              <input id="email" value={values.email} onChange={handleChange} />
              <button type="submit">Submit</button>
          </form>
      )
  }
  ```

  ```svelte Svelte 4 icon="s" theme={null}
  <script>
  import { router } from '@inertiajs/svelte'

  let values = {
      first_name: null,
      last_name: null,
      email: null,
  }

  function submit() {
      router.post('/users', values)
  }
  </script>

  <form on:submit|preventDefault={submit}>
      <label for="first_name">First name:</label>
      <input id="first_name" bind:value={values.first_name}>

      <label for="last_name">Last name:</label>
      <input id="last_name" bind:value={values.last_name}>

      <label for="email">Email:</label>
      <input id="email" bind:value={values.email}>

      <button type="submit">Submit</button>
  </form>
  ```

  ```svelte Svelte 5 icon="s" theme={null}
  <script>
  import { router } from '@inertiajs/svelte'

  let values = {
      first_name: null,
      last_name: null,
      email: null,
  }

  function submit(e) {
      e.preventDefault()
      router.post('/users', values)
  }
  </script>

  <form onsubmit={submit}>
      <label for="first_name">First name:</label>
      <input id="first_name" bind:value={values.first_name}>

      <label for="last_name">Last name:</label>
      <input id="last_name" bind:value={values.last_name}>

      <label for="email">Email:</label>
      <input id="email" bind:value={values.email}>

      <button type="submit">Submit</button>
  </form>
  ```
</CodeGroup>

## Tải file lên

Khi request hoặc submit form có file, Inertia tự động chuyển dữ liệu request thành object `FormData`. Cơ chế này hoạt động với component `<Form>`, helper `useForm` và submit thủ công bằng router.

Để biết thêm về tải file, bao gồm theo dõi tiến trình, hãy xem [tài liệu tải file](/v2/the-basics/file-uploads).

## Submit bằng XHR / Fetch

Dùng Inertia để submit form phù hợp với phần lớn tình huống. Tuy nhiên, nếu cần kiểm soát quá trình submit chi tiết hơn, bạn có thể thực hiện request XHR hoặc `fetch` thuần bằng thư viện mình chọn.

***

## Tài liệu chính thức

Bài dịch này được đối chiếu từ [tài liệu Inertia.js v2 chính thức](https://inertiajs.com/docs/v2/the-basics/forms). Nếu có khác biệt do phiên bản hoặc cập nhật mới, hãy ưu tiên tài liệu chính thức làm nguồn tham chiếu.
