diff --git a/docs/guide/events.md b/docs/guide/events.md
index 8ce9f962..699f0f9b 100644
--- a/docs/guide/events.md
+++ b/docs/guide/events.md
@@ -228,7 +228,7 @@ document.removeEventListener('inertia:start', startEventListener)
## Cancelling events
-Some events, such as `before`, `invalid`, and `error`, support cancellation, allowing you to prevent Inertia's default behavior. Just like native events, the event will be cancelled if only one event listener calls `event.preventDefault()`.
+Some events, such as `before`, `exception`, and `invalid`, support cancellation, allowing you to prevent Inertia's default behavior. Just like native events, the event will be cancelled if only one event listener calls `event.preventDefault()`.
:::tabs key:frameworks
== Vue
diff --git a/docs/guide/forms.md b/docs/guide/forms.md
index 3f7d4d3d..ffaabd6a 100644
--- a/docs/guide/forms.md
+++ b/docs/guide/forms.md
@@ -1,194 +1,943 @@
# Forms
-## Submitting forms
+Inertia provides two primary ways to build forms: the `
+
+
+
+
+)
+```
+
+Just like a traditional HTML form, there is no need to attach an `onChange` handler to your input fields, just give each input a `name` attribute and a `defaultValue` (if applicable) and the `Form` component will handle the data submission for you.
+
+== Svelte 4 | Svelte 5
+
+```svelte
+
-
-
-
-
+
+
+
+
+
+```
-
-
+Just like a traditional HTML form, there is no need to attach a `bind:` to your input fields, just give each input a `name` attribute and the `Form` component will handle the data submission for you.
-
-
+:::
-
-
+The component also supports advanced use cases, including nested data structures, file uploads, and dotted key notation.
+
+:::tabs key:frameworks
+
+== Vue
+
+```vue
+
+
+
+
+
+
+
+```
+
+== React
+
+```jsx
+
+
+
+
+
+
+
+```
+
+== Svelte 4 | Svelte 5
+
+```svelte
+
+
+
+
+
+
+
+```
+
+:::
+
+You can pass a `transform` prop to modify the form data before submission. This is useful for injecting additional fields or transforming existing data, although hidden inputs work too.
+
+:::tabs key:frameworks
+
+== Vue
+
+```vue
+
+
+
+
+```
+
+== React
+
+```jsx
+
({ ...data, user_id: 123 })}
+>
+
+
+
+```
+
+== Svelte 4 | Svelte 5
+
+```svelte
+
({ ...data, user_id: 123 })}
+>
+
+
+
+```
+
+:::
+
+### Checkbox inputs
+
+When working with checkboxes, you may want to add an explicit `value` attribute such as `value="1"`. Without a value attribute, checked checkboxes will submit as `"on"`, which some server-side validation rules may not recognize as a proper boolean value.
+
+### Slot props
+
+The `
` component exposes reactive state and helper methods through its default slot, giving you access to form processing state, errors, and utility functions.
+
+:::tabs key:frameworks
+
+== Vue
+
+```vue
+
+
+
+
{{ errors.name }}
+
+
+
+
User created successfully!
+
```
== React
```jsx
-import { useState } from 'react'
-import { router } from '@inertiajs/react'
+
+ {/if}
+ {/snippet}
+
+```
+
+:::
+
+#### `defaults` method
+
+@available_since core=2.1.1
+
+The `defaults` method allows you to update the form's default values to match the current field values. When called, subsequent `reset()` calls will restore fields to these new defaults, and the `isDirty` property will track changes from these updated defaults. Unlike `useForm`, this method accepts no arguments and always uses all current form values.
+
+#### `errors` object
+
+The `errors` object uses dotted notation for nested fields, allowing you to display validation messages for complex form structures.
+
+:::tabs key:frameworks
+
+== Vue
+
+```vue
+
-
-
+:::
-
-
+### Props and options
-
-
+In addition to `action` and `method`, the `
` component accepts several props. Many of them are identical to the options available in Inertia's [visit options](/guide/manual-visits).
-
+:::tabs key:frameworks
+
+== Vue
+
+```vue
+
+
+
+
+```
+
+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 subsequent visit.
+
+When setting the `disable-while-processing` prop, the `Form` component will add the `inert` attribute to the HTML `form` tag while the form is processing to prevent user interaction.
+
+== React
+
+```jsx
+
+```
+
+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 subsequent visit.
+
+When setting the `disableWhileProcessing` prop, the `Form` component will add the `inert` attribute to the HTML `form` tag while the form is processing to prevent user interaction.
+
+== Svelte 4 | Svelte 5
+
+```svelte
+
+```
+
+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 subsequent visit.
+
+When setting the `disableWhileProcessing` prop, the `Form` component will add the `inert` attribute to the HTML `form` tag while the form is processing to prevent user interaction.
+
+:::
+
+To style the form while it's processing, you can target the inert form in the following ways:
+
+:::tabs key:css
+
+== Tailwind 4
+
+```html
+
` component emits all the standard visit [events](/guide/events) for form submissions:
+
+:::tabs key:frameworks
+
+== Vue
+
+```vue
+
+
+
+
+```
+
+== React
+
+```jsx
+
+
+
+
+```
+
+== Svelte 4
+
+```svelte
+
+
+
+
+```
+
== Svelte 5
```svelte
-
-
-
-
+
+
+
+
+
-
-
+
+
+```
-
-
+== React
-
-
-```
+```jsx
+import { useRef } from 'react'
+import { Form } from '@inertiajs/react'
-:::
+export default function CreateUser() {
+ const formRef = useRef()
-As you may have noticed in the example above, when using Inertia, you don't typically need to inspect form responses client-side like you would when making XHR / fetch requests manually.
+ const handleSubmit = () => {
+ formRef.current.submit()
+ }
-Instead, your server-side route / controller typically issues a [redirect](/guide/redirects.md) response. And, Of course, there is nothing stopping you from redirecting the user right back to the page they were previously on. Using this approach, handling Inertia form submissions feels very similar to handling classic HTML form submissions.
+ return (
+ <>
+
-Handling server-side validation errors in Inertia works a little different than handling errors from manual XHR / fetch requests. When making XHR / fetch requests, you typically inspect the response for a `422` status code and manually update the form's error state.
+
+```
-However, when using Inertia, a `422` response is never returned by your server. Instead, as we saw in the example above, your routes / controllers will typically return a redirect response - much like a classic, full-page form submission.
+:::
-For a full discussion on handling and displaying [validation](/guide/validation.md) errors with Inertia, please consult the validation documentation.
+In React and Vue, refs provide access to all form methods and reactive state. In Svelte, refs expose only methods, so reactive state like isDirty and errors should be accessed via [slot props](#slot-props) instead.
## Form helper
-Since working with forms is so common, Inertia includes a form helper designed to help reduce the amount of boilerplate code needed for handling typical form submissions. The `useForm` method provides a convenient way to manage form state, validation, and submission.
+In addition to the `
` component, Inertia also provides a `useForm` helper for when you need programmatic control over your form's data and submission behavior:
:::tabs key:frameworks
+
== Vue
```vue
@@ -653,6 +1402,46 @@ $form.reset('field', 'anotherfield')
:::
+@available_since core=2.0.15
+
+Sometimes, you may want to restore your form fields to their default values and clear any validation errors at the same time. Instead of calling `reset()` and `clearErrors()` separately, you can use the `resetAndClearErrors()` method, which combines both actions into a single call.
+
+:::tabs key:frameworks
+
+== Vue
+
+```js
+// Reset the form and clear all errors...
+form.resetAndClearErrors()
+
+// Reset specific fields and clear their errors...
+form.resetAndClearErrors('field', 'anotherfield')
+```
+
+== React
+
+```jsx
+const { resetAndClearErrors } = useForm({ ... })
+
+// Reset the form and clear all errors...
+resetAndClearErrors()
+
+// Reset specific fields and clear their errors...
+resetAndClearErrors('field', 'anotherfield')
+```
+
+== Svelte 4|Svelte 5
+
+```js
+// Reset the form and clear all errors...
+$form.resetAndClearErrors()
+
+// Reset specific fields and clear their errors...
+$form.resetAndClearErrors('field', 'anotherfield')
+```
+
+:::
+
If your form's default values become outdated, you can use the `defaults()` method to update them. Then, the form will be reset to the correct values the next time the `reset()` method is invoked.
:::tabs key:frameworks
@@ -792,12 +1581,193 @@ const form = useForm(`EditUser:${user.id}`, data)
:::
+## Server-side responses
+
+When using Inertia, you don't typically inspect form responses client-side like you would with traditional XHR/fetch requests. Instead, your server-side route or controller issues a [redirect](/guide/redirects) response after processing the form, often redirecting to a success page.
+
+```ruby
+class UsersController < ApplicationController
+ def create
+ user = User.new(user_params)
+
+ if user.save
+ redirect_to users_url
+ else
+ redirect_to new_user_url, inertia: { errors: user.errors }
+ end
+ end
+
+ private
+
+ def user_params
+ params.require(:user).permit(:name, :email)
+ end
+end
+```
+
+This redirect-based approach works with all form submission methods: the `
` component, `useForm` helper, and manual router submissions. It makes handling Inertia forms feel very similar to classic server-side form submissions.
+
+## Server-side validation
+
+Both the `
` component and `useForm` helper automatically handle server-side validation errors. When your server returns validation errors, they're automatically available in the `errors` object without any additional configuration.
+
+Unlike traditional XHR/fetch requests where you'd check for a `422` status code, Inertia handles validation errors as part of its redirect-based flow, just like classic server-side form submissions, but without the full page reload.
+
+For a complete guide on validation error handling, including error bags and advanced scenarios, see the [validation documentation](/guide/validation).
+
+## Manual form submissions
+
+It's also possible to submit forms manually using Inertia's `router` methods directly, without using the `
+```
+
+:::
+
## File uploads
-When making requests or form submissions that include files, Inertia will automatically convert the request data into a `FormData` object.
+When making requests or form submissions that include files, Inertia will automatically convert the request data into a `FormData` object. This works with the `
` component, `useForm` helper, and manual router submissions.
-For a more thorough discussion of file uploads, please consult the [file uploads documentation](/guide/file-uploads.md).
+For more information on file uploads, including progress tracking, see the [file uploads documentation](/guide/file-uploads).
## XHR / fetch submissions
-Using Inertia to submit forms works great for the vast majority of situations; however, in the event that you need more control over the form submission, you're free to make plain XHR or `fetch` requests instead using the library of your choice.
+Using Inertia to submit forms works great for the vast majority of situations. However, in the event that you need more control over the form submission, you're free to make plain XHR or `fetch` requests instead, using the library of your choice.
diff --git a/docs/guide/manual-visits.md b/docs/guide/manual-visits.md
index 1002b6ca..4bcca6e5 100644
--- a/docs/guide/manual-visits.md
+++ b/docs/guide/manual-visits.md
@@ -417,6 +417,10 @@ router.push({
encryptHistory: false,
preserveScroll: false,
preserveState: false,
+ errorBag: null,
+ onSuccess: (page) => {},
+ onError: (errors) => {},
+ onFinish: (visit) => {},
})
```
@@ -433,6 +437,10 @@ router.push({
encryptHistory: false,
preserveScroll: false,
preserveState: false,
+ errorBag: null,
+ onSuccess: (page) => {},
+ onError: (errors) => {},
+ onFinish: (visit) => {},
})
```
@@ -449,15 +457,21 @@ router.push({
encryptHistory: false,
preserveScroll: false,
preserveState: false,
+ errorBag: null,
+ onSuccess: (page) => {},
+ onError: (errors) => {},
+ onFinish: (visit) => {},
})
```
:::
-All the parameters are optional. By default, all passed parameters will be merged with the current page. This means you are responsible for overriding the current page's URL, component, and props.
+All the parameters are optional. By default, all passed parameters (except `errorBag`) will be merged with the current page. This means you are responsible for overriding the current page's URL, component, and props.
If you need access to the current page's props you can pass a function to the props option. This function will receive the current page's props as an argument and should return the new props.
+The `errorBag` option allows you to specify which error bag to use when handling validation errors in the `onError` callback.
+
:::tabs key:frameworks
== Vue
diff --git a/docs/guide/prefetching.md b/docs/guide/prefetching.md
index 0a227e6e..2df9c9bd 100644
--- a/docs/guide/prefetching.md
+++ b/docs/guide/prefetching.md
@@ -227,7 +227,7 @@ router.prefetch(
)
```
-To make this even easier, Inertia offers a prefetch helper. This helper provides some additional insight into the request, such as the last updated timestamp and if the request is currently prefetching.
+Inertia also provides a `usePrefetch` hook that allows you to track the prefetch state for the current page. It returns information about whether the page is currently prefetching, has been prefetched, when it was last updated, and a `flush` method that flushes the cache for the current page only.
:::tabs key:frameworks
== Vue
@@ -235,11 +235,7 @@ To make this even easier, Inertia offers a prefetch helper. This helper provides
```js
import { usePrefetch } from '@inertiajs/vue3'
-const { lastUpdatedAt, isPrefetching, isPrefetched } = usePrefetch(
- '/users',
- { method: 'get', data: { page: 2 } },
- { cacheFor: '1m' },
-)
+const { lastUpdatedAt, isPrefetching, isPrefetched, flush } = usePrefetch()
```
== React
@@ -247,11 +243,7 @@ const { lastUpdatedAt, isPrefetching, isPrefetched } = usePrefetch(
```js
import { usePrefetch } from '@inertiajs/react'
-const { lastUpdatedAt, isPrefetching, isPrefetched } = usePrefetch(
- '/users',
- { method: 'get', data: { page: 2 } },
- { cacheFor: '1m' },
-)
+const { lastUpdatedAt, isPrefetching, isPrefetched, flush } = usePrefetch()
```
== Svelte 4|Svelte 5
@@ -259,22 +251,106 @@ const { lastUpdatedAt, isPrefetching, isPrefetched } = usePrefetch(
```js
import { usePrefetch } from '@inertiajs/svelte'
-const { lastUpdatedAt, isPrefetching, isPrefetched } = usePrefetch(
- '/users',
- { method: 'get', data: { page: 2 } },
- { cacheFor: '1m' },
-)
+const { lastUpdatedAt, isPrefetching, isPrefetched, flush } = usePrefetch()
+```
+
+:::
+
+You can also pass visit options when you need to differentiate between different request configurations for the same URL.
+
+:::tabs key:frameworks
+
+== Vue
+
+```js
+import { usePrefetch } from '@inertiajs/vue3'
+
+const { lastUpdatedAt, isPrefetching, isPrefetched, flush } = usePrefetch({
+ headers: { 'X-Custom-Header': 'value' },
+})
+```
+
+== React
+
+```js
+import { usePrefetch } from '@inertiajs/react'
+
+const { lastUpdatedAt, isPrefetching, isPrefetched, flush } = usePrefetch({
+ headers: { 'X-Custom-Header': 'value' },
+})
+```
+
+== Svelte 4|Svelte 5
+
+```js
+import { usePrefetch } from '@inertiajs/svelte'
+
+const { lastUpdatedAt, isPrefetching, isPrefetched, flush } = usePrefetch({
+ headers: { 'X-Custom-Header': 'value' },
+})
```
:::
-## Flushing prefetch cache
+## Cache tags
+
+@available_since core=2.1.2
+
+Cache tags allow you to group related prefetched data and invalidate it all at once when specific events occur.
+
+To tag cached data, pass a `cacheTags` prop to your `Link` component.
+
+:::tabs key:frameworks
+
+== Vue
+
+```vue
+
+
+
+ Users
+
+ Dashboard
+
+
+```
-You can flush the prefetch cache by calling `router.flushAll`. This will remove all cached data for all pages.
+== React
-If you want to flush the cache for a specific page, you can pass the page URL and options to the `router.flush` method.
+```jsx
+import { Link } from '@inertiajs/react'
+
+Users
+Dashboard
+```
-Furthermore, if you are using the prefetch helper, it will return a `flush` method for you to use for that specific page.
+== Svelte 4 | Svelte 5
+
+```svelte
+import {inertia} from '@inertiajs/svelte'
+
+Users
+Dashboard
+```
+
+:::
+
+When prefetching programmatically, pass `cacheTags` in the third argument to `router.prefetch`.
+
+```js
+router.prefetch('/users', {}, { cacheTags: 'users' })
+router.prefetch('/dashboard', {}, { cacheTags: ['dashboard', 'stats'] })
+```
+
+## Cache invalidation
+
+You can manually flush the prefetch cache by calling `router.flushAll` to remove all cached data, or `router.flush` to remove cache for a specific page.
```js
// Flush all prefetch cache
@@ -283,9 +359,151 @@ router.flushAll()
// Flush cache for a specific page
router.flush('/users', { method: 'get', data: { page: 2 } })
-// Flush cache for a specific page
-const { flush } = usePrefetch('/users', { method: 'get', data: { page: 2 } })
-flush()
+// Using the usePrefetch hook
+const { flush } = usePrefetch()
+
+flush() // Flush cache for the current page
+```
+
+For more granular control, you can flush cached data by their tags using `router.flushByCacheTags`. This removes any cached response that contains _any_ of the specified tags.
+
+```js
+// Flush all responses tagged with 'users'
+router.flushByCacheTags('users')
+
+// Flush all responses tagged with 'dashboard' OR 'stats'
+router.flushByCacheTags(['dashboard', 'stats'])
+```
+
+### Invalidate on requests
+
+@available_since core=2.1.2
+
+To automatically invalidate caches when making requests, pass an `invalidateCacheTags` prop to the Form component. The specified tags will be flushed when the form submission succeeds.
+
+:::tabs key:frameworks
+
+== Vue
+
+```vue
+
+
+
+
+
+
+
+
+
+```
+
+== React
+
+```jsx
+import { Form } from '@inertiajs/react'
+
+export default () => (
+