diff --git a/resources/js/Pages/forms.jsx b/resources/js/Pages/forms.jsx index 2658a39b..9ef8aa61 100644 --- a/resources/js/Pages/forms.jsx +++ b/resources/js/Pages/forms.jsx @@ -4,9 +4,11 @@ import dedent from 'dedent-js' export const meta = { title: 'Forms', links: [ - { url: '#submitting-forms', name: 'Submitting forms' }, - { url: '#server-side-validation', name: 'Server-side validation' }, + { url: '#form-component', name: 'Form component' }, { url: '#form-helper', name: 'Form helper' }, + { url: '#server-side-responses', name: 'Server-side responses' }, + { url: '#server-side-validation', name: 'Server-side validation' }, + { url: '#manual-form-submissions', name: 'Manual form submissions' }, { url: '#file-uploads', name: 'File uploads' }, { url: '#xhr-fetch-submissions', name: 'XHR / fetch submissions' }, ], @@ -16,11 +18,14 @@ export default function () { return ( <>

Forms

-

Submitting forms

- While it's possible to make classic HTML form submissions with Inertia, it's not recommended since they cause - full-page reloads. Instead, it's better to intercept form submissions and then make the{' '} - request using Inertia. + Inertia provides two primary ways to build forms: the <Form> component and the useForm helper. + Both integrate with your server-side framework's validation and handle form submissions without full page reloads. +

+

Form component

+

+ Inertia provides a <Form> component that behaves much like a classic HTML form, but uses + Inertia under the hood to avoid full page reloads. This is the simplest way to get started with forms in Inertia:

- 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) - } - - - +
+ + + +
`, }, { name: 'React', language: 'jsx', code: dedent` - import { useState } from 'react' - import { router } from '@inertiajs/react' - - export default function Edit() { - const [values, setValues] = useState({ - first_name: "", - last_name: "", - email: "", - }) +
+ + + +
+ `, + }, + { + name: 'Svelte', + language: 'html', + code: dedent` +
+ + + +
+ `, + }, + ]} + /> +

+ The component also supports advanced use cases, including nested data structures, file uploads, and dotted key notation: +

+ + + + + + + + `, + }, + { + name: 'React', + language: 'jsx', + code: dedent` +
+ + + + + +
+ `, + }, + { + name: 'Svelte', + language: 'html', + code: dedent` +
+ + + + + +
+ `, + }, + ]} + /> +

+ 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: +

+ + + + + `, + }, + { + name: 'React', + language: 'jsx', + code: dedent` +
({ ...data, user_id: 123 })} + > + + +
+ `, + }, + { + name: 'Svelte', + language: 'html', + code: dedent` +
({ ...data, user_id: 123 })} + > + + +
+ `, + }, + ]} + /> +

Slot props

+

+ The <Form> component exposes reactive state and helper methods through its default slot, + giving you access to form processing state, errors, and utility functions: +

+ + +
{{ errors.name }}
+ + + +
User created successfully!
+ + `, + }, + { + name: 'React', + language: 'jsx', + code: dedent` +
+ {({ + errors, + hasErrors, + processing, + progress, + wasSuccessful, + recentlySuccessful, + setError, + clearErrors, + resetAndClearErrors, + isDirty, + reset, + submit, + }) => ( + <> + + {errors.name &&
{errors.name}
} + + + + {wasSuccessful &&
User created successfully!
} + + )} +
+ `, + }, + { + name: 'Svelte 4', + language: 'html', + code: dedent` +
+ + {#if errors.name} +
{errors.name}
+ {/if} - 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) + {#if wasSuccessful} +
User created successfully!
+ {/if} +
+ `, + }, + { + name: 'Svelte 5', + language: 'html', + code: dedent` +
+ {#snippet children({ + errors, + hasErrors, + processing, + progress, + wasSuccessful, + recentlySuccessful, + setError, + clearErrors, + resetAndClearErrors, + isDirty, + reset, + submit, + })} + + {#if errors.name} +
{errors.name}
+ {/if} + + + + {#if wasSuccessful} +
User created successfully!
+ {/if} + {/snippet} +
+ `, + }, + ]} + /> +

+ The errors object uses dotted notation for nested fields, allowing you to display validation + messages for complex form structures: +

+ + +
{{ errors['user.name'] }}
+ + `, + }, + { + name: 'React', + language: 'jsx', + code: dedent` +
+ {({ errors }) => ( + <> + + {errors['user.name'] &&
{errors['user.name']}
} + + )} +
+ `, + }, + { + name: 'Svelte 4', + language: 'html', + code: dedent` +
+ + {#if errors['user.name']} +
{errors['user.name']}
+ {/if} +
+ `, + }, + { + name: 'Svelte 5', + language: 'html', + code: dedent` +
+ {#snippet children({ errors })} + + {#if errors['user.name']} +
{errors['user.name']}
+ {/if} + {/snippet} +
+ `, + }, + ]} + /> +

Props and options

+

+ In addition to action and method, the <Form> component accepts + several props. Many of them are identical to the options available in Inertia's{' '} + visit options: +

+ + + + + `, + }, + { + name: 'React', + language: 'jsx', + code: dedent` +
({ ...data, timestamp: Date.now() })} + options={{ + preserveScroll: true, + preserveState: true, + preserveUrl: true, + replace: true, + only: ['users', 'flash'], + except: ['secret'], + reset: ['page'], + }} + > + + +
+ `, + }, + { + name: 'Svelte', + language: 'html', + code: dedent` +
({ ...data, timestamp: Date.now() })} + options={{ + preserveScroll: true, + preserveState: true, + preserveUrl: true, + replace: true, + only: ['users', 'flash'], + except: ['secret'], + reset: ['page'], + }} + > + + +
+ `, + }, + ]} + /> +

+ 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. +

+

Events

+

+ The <Form> component emits all the standard visit events for form + submissions, plus a cancelToken event for handling form cancellation: +

+ + + + + `, + }, + { + name: 'React', + language: 'jsx', + code: dedent` +
+ + +
+ `, + }, + { + name: 'Svelte 4', + language: 'html', + code: dedent` +
+ + +
+ `, + }, + { + name: 'Svelte 5', + language: 'html', + code: dedent` +
+ + +
+ `, + }, + ]} + /> +

Dotted key notation

+

+ The <Form> component supports dotted key notation for creating nested objects from flat + input names. This provides a convenient way to structure form data. +

+ + + + + + + `, + }, + { + name: 'React', + language: 'jsx', + code: dedent` +
+ + + + +
+ `, + }, + { + name: 'Svelte', + language: 'html', + code: dedent` +
+ + + + +
+ `, + }, + ]} + /> +

+ The above example would generate the following data structure: +

+ - - - - - - - - - ) } `, }, + ]} + /> +

+ If you need literal dots in your field names (not as nested object separators), you can escape them using + backslashes: +

+ + + + + + `, + }, + { + name: 'React', + language: 'jsx', + code: dedent` +
+ + + +
+ `, + }, + { + name: 'Svelte', language: 'html', code: dedent` - -
- - + + `, + }, + { + name: 'React', + language: 'jsx', + code: dedent` + import { useRef } from 'react' + import { Form } from '@inertiajs/react' + + export default function CreateUser() { + const formRef = useRef() - - + const handleSubmit = () => { + formRef.current.submit() + } - -
+ return ( + <> +
+ + +
+ + + + ) + } `, }, { - name: 'Svelte 5', + name: 'Svelte', language: 'html', code: dedent` -
- - - - - - - - - + + -
- `, - }, - ]} - /> -

- 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. -

-

- Instead, your server-side route / controller typically issues a redirect 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. -

- 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'); - } - } + `, }, ]} /> -

Server-side validation

- 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 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 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. + In addition to the <Form> component, Inertia also provides a useForm helper for + when you need programmatic control over your form's data and submission behavior:

The submit methods support all of the typical visit options, such as{' '} preserveState, preserveScroll, and event callbacks, which can be helpful for - performing tasks on successful form submissions. For example, you might use the onSuccess callback + performing tasks on successful form submissions. For example, you might use the onSuccess{' '}callback to reset inputs to their original state.

- Sometimes, you may want to restore your form fields to their default values and clear any validation errors at{' '} + 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.

@@ -1005,21 +1553,226 @@ export default function () { }, ]} /> +

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 response after + processing the form, often redirecting to a success page. +

+ 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'); + } + } + `, + }, + ]} + /> +

+ This redirect-based approach works with all form submission methods: the <Form> 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 <Form> 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. +

+

Manual form submissions

+

+ It's also possible to submit forms manually using Inertia's router methods directly, without using + the <Form> component or useForm helper: +

+ + 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) + } + + + + `, + }, + { + name: 'React', + language: 'jsx', + code: dedent` + 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 ( +
+ + + + + + + +
+ ) + } + `, + }, + { + name: 'Svelte 4', + language: 'html', + code: dedent` + + +
+ + + + + + + + + + +
+ `, + }, + { + name: 'Svelte 5', + language: 'html', + code: dedent` + + +
+ + + + + + + + + + +
+ `, + }, + ]} + />

File uploads

When making requests or form submissions that include files, Inertia will automatically convert the request data - into a FormData object. + into a FormData object. This works with the <Form> component, useForm{' '} + helper, and manual router submissions.

- For a more thorough discussion of file uploads, please consult the{' '} + For more information on file uploads, including progress tracking, see the{' '} file uploads documentation.

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 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.

) -} +} \ No newline at end of file