diff --git a/resources/js/Pages/forms.jsx b/resources/js/Pages/forms.jsx index f5eed89f..d03880b9 100644 --- a/resources/js/Pages/forms.jsx +++ b/resources/js/Pages/forms.jsx @@ -5,6 +5,7 @@ export const meta = { title: 'Forms', links: [ { url: '#form-component', name: 'Form component' }, + { url: '#precognition', name: 'Precognition' }, { url: '#form-helper', name: 'Form helper' }, { url: '#server-side-responses', name: 'Server-side responses' }, { url: '#server-side-validation', name: 'Server-side validation' }, @@ -1096,6 +1097,588 @@ export default function () { methods, so reactive state like isDirty and errors should be accessed via{' '} slot props instead.

+

Precognition

+

+ The {'

'} component includes built-in support for precognition, enabling real-time form + validation without duplicating your server-side validation rules on the client. +

+

+ Before using precognition, ensure your server-side framework is configured to handle precognitive requests. + Laravel users can follow the Precognition documentation for + setup instructions. Other frameworks can implement precognition support by following the{' '} + precognition protocol. +

+

Basic validation

+

+ Once your server is configured, call validate() with a field name to trigger validation for that + field. The invalid() helper checks if a field has validation errors, while validating{' '} + indicates when a request is in progress. +

+ + + +

{{ errors.name }}

+ + + +

{{ errors.email }}

+ +

Validating...

+ + + + `, + }, + { + name: 'React', + language: 'jsx', + code: dedent` +
+ {({ errors, invalid, validate, validating }) => ( + <> + + validate('name')} /> + {invalid('name') &&

{errors.name}

} + + + validate('email')} /> + {invalid('email') &&

{errors.email}

} + + {validating &&

Validating...

} + + + + )} +
+ `, + }, + { + name: 'Svelte 4', + language: 'html', + code: dedent` +
+ + validate('name')} /> + {#if invalid('name')} +

{errors.name}

+ {/if} + + + validate('email')} /> + {#if invalid('email')} +

{errors.email}

+ {/if} + + {#if validating} +

Validating...

+ {/if} + + +
+ `, + }, + { + name: 'Svelte 5', + language: 'html', + code: dedent` +
+ {#snippet children({ errors, invalid, validate, validating })} + + validate('name')} /> + {#if invalid('name')} +

{errors.name}

+ {/if} + + + validate('email')} /> + {#if invalid('email')} +

{errors.email}

+ {/if} + + {#if validating} +

Validating...

+ {/if} + + + {/snippet} +
+ `, + }, + ]} + /> +

+ You can also use the valid() helper to check if a field has passed validation. This is useful for + showing success states or visual indicators. +

+ + +

Valid email address

+

{{ errors.email }}

+ + `, + }, + { + name: 'React', + language: 'jsx', + code: dedent` +
+ {({ errors, invalid, valid, validate }) => ( + <> + validate('email')} /> + {valid('email') &&

Valid email address

} + {invalid('email') &&

{errors.email}

} + + )} +
+ `, + }, + { + name: 'Svelte', + language: 'html', + code: dedent` +
+ validate('email')} /> + {#if valid('email')} +

Valid email address

+ {/if} + {#if invalid('email')} +

{errors.email}

+ {/if} +
+ `, + }, + ]} + /> +

Debouncing

+

+ Validation requests are automatically debounced. The first validation request for a field fires immediately to + provide instant feedback. Subsequent changes are then debounced, waiting 1500ms before sending another request. + You can configure the debounce timeout using the{' '} + + validateTimeout + + + validateTimeout + + + validate-timeout + {' '} + prop. +

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

Validating multiple fields

+

+ You can validate multiple fields at once by passing an array to validate(). This is useful when you + want to validate an entire section of a form at once. +

+ + +

{{ errors.name }}

+ + +

{{ errors.email }}

+ + + + `, + }, + { + name: 'React', + language: 'jsx', + code: dedent` +
+ {({ validate, invalid, errors }) => ( + <> + + {invalid('name') &&

{errors.name}

} + + + {invalid('email') &&

{errors.email}

} + + + + )} +
+ `, + }, + { + name: 'Svelte', + language: 'html', + code: dedent` +
+ + {#if invalid('name')} +

{errors.name}

+ {/if} + + + {#if invalid('email')} +

{errors.email}

+ {/if} + + +
+ `, + }, + ]} + /> +

Touch and validate

+

+ The touch() method marks fields as "touched" without triggering validation. This is useful for + tracking which fields the user has interacted with. You can then validate all touched fields at once by calling{' '} + validate() without arguments. +

+ + + + + + + +

Name has been touched

+ + `, + }, + { + name: 'React', + language: 'jsx', + code: dedent` +
+ {({ validate, touch, touched }) => ( + <> + touch('name')} /> + touch('email')} /> + touch('phone')} /> + + + + {touched('name') &&

Name has been touched

} + + )} +
+ `, + }, + { + name: 'Svelte', + language: 'html', + code: dedent` +
+ touch('name')} /> + touch('email')} /> + touch('phone')} /> + + + + {#if touched('name')} +

Name has been touched

+ {/if} +
+ `, + }, + ]} + /> +

+ The touched() helper can also be called with a specific field name to check if that field has been + touched. In addition to resetting one or more values with the reset() method, it also clears the + touched state for those fields. +

+

File validation

+

+ By default, files are not included in precognitive validation requests to avoid unnecessary uploads. If you need + to validate file fields, set{' '} + + validateFiles={'{true}'} + + + validateFiles={'{true}'} + + + :validate-files="true" + {' '} + on the form. This will send files to the server during validation, allowing you to validate file size, type, and + other attributes. +

+ + +

{{ errors.avatar }}

+ + `, + }, + { + name: 'React', + language: 'jsx', + code: dedent` +
+ {({ validate, invalid, errors }) => ( + <> + validate('avatar')} /> + {invalid('avatar') &&

{errors.avatar}

} + + )} +
+ `, + }, + { + name: 'Svelte', + language: 'html', + code: dedent` +
+ validate('avatar')} /> + {#if invalid('avatar')} +

{errors.avatar}

+ {/if} +
+ `, + }, + ]} + /> +

Validation callbacks

+

+ The validate() method accepts several callbacks that let you respond to validation results. Use{' '} + onSuccess when validation passes, onError when it fails, and onFinish{' '} + when validation completes regardless of the result. +

+ { + // Hide loading indicator, show success icon, etc. + }, + onError: (errors) => { + // Log errors, show notification, etc. + console.error(errors.username) + }, + onFinish: () => { + // Always runs after validation + }, + }) + `, + }, + ]} + /> +

+ The onBefore callback allows you to intercept validation requests before they're sent to the + server. Return false to prevent validation from occurring. This is useful for implementing + conditional validation logic or rate limiting. +

+ { + // Only validate if username meets minimum length + if (newRequest.data.username.length < 3) { + return false + } + }, + }) + `, + }, + ]} + /> +

+ For handling unexpected server errors during validation (non-422 responses), use the onException{' '} + callback. This allows you to gracefully handle network errors, server errors, or other unexpected issues. +

+ { + console.error('Validation request failed:', error) + // Show user-friendly error message + alert('Unable to validate. Please check your connection.') + }, + }) + `, + }, + ]} + /> +

Validation options

+

+ Instead of passing fields as an array, you can pass an options object with only to specify which + fields to validate. This also allows you to include other options like headers and{' '} + errorBag. +

+ { + console.log('Validation passed') + }, + }) + `, + }, + ]} + /> +

Error formatting

+

+ Laravel validation errors are typically arrays of messages per field. For example, a field might have multiple + validation rules that each produce an error. Normally, Laravel's Inertia middleware simplifies these to single + strings (the first error only) before sending them to the client. However, during precognitive validation, the{' '} + 422 response returns early (without the Inertia middleware handling it) so the{' '} + {'

'} component handles this simplification on the frontend instead. +

+

+ By default, validation errors are automatically simplified to strings. If you need access to all error messages + for a field, you can disable this behavior by setting{' '} + + simpleValidationErrors={'{false}'} + + + simpleValidationErrors={'{false}'} + + + :simple-validation-errors="false" + {' '} + on the form. +

+

Canceling validation

+

+ Validation requests are automatically canceled when a new validation is triggered for the same form, preventing + race conditions and ensuring only the most recent validation is processed. You can also manually cancel pending + validation requests using the cancelValidation() method. +

+ + + + + `, + }, + { + name: 'React', + language: 'jsx', + code: dedent` +
+ {({ cancelValidation }) => ( + <> + {/* ... */} + + + )} +
+ `, + }, + { + name: 'Svelte', + language: 'html', + code: dedent` +
+ + +
+ `, + }, + ]} + />

Form helper

In addition to the {'

'} component, Inertia also provides a useForm helper for diff --git a/resources/js/Pages/the-protocol.jsx b/resources/js/Pages/the-protocol.jsx index 7db6967e..b1fe21ff 100644 --- a/resources/js/Pages/the-protocol.jsx +++ b/resources/js/Pages/the-protocol.jsx @@ -182,6 +182,18 @@ export default function () { appended or prepended when using Infinite scroll. +

+ The following headers are used for precognitive form validation. +

+
    +
  1. + Precognition: Set to true to indicate this is a precognitive validation request. +
  2. +
  3. + Precognition-Validate-Only: Comma-separated list of field names to validate during{' '} + precognition requests. +
  4. +

Response headers

The following headers should be sent by your server-side adapter in Inertia responses. If you're using an @@ -519,6 +531,19 @@ export default function () { X-Inertia-Location header and triggers a window.location redirect client-side. +

+ The following status codes are used for precognitive form validation. +

+
    +
  1. + 204 No Content: Used for successful precognition validation requests when no validation + errors are found. +
  2. +
  3. + 422 Unprocessable Entity: Used for precognition validation requests when validation errors + are found. The response body should contain the validation errors. +
  4. +
) }