Validation UX Philosophy
Relevant source files
The following files were used as context for generating this wiki page:The @opensky/remotes package provides helpers to improve the user experience of SvelteKit's remote form functions. The core of its validation philosophy is to provide feedback that is timely but not intrusive. This is achieved by showing validation issues only after a user has finished interacting with a field (on blur), and clearing them as soon as they start typing again (on input). This prevents users from seeing distracting error messages appear and disappear as they type.
This approach is primarily implemented through the createValidation helper, which works in tandem with SvelteKit's RemoteForm. It is designed to be used alongside the createEnhancedForm helper, which automates the integration between the form submission lifecycle and the validation state, creating a seamless and robust validation experience.
Sources: README.md:3-8, packages/remotes/src/lib/validation.svelte.ts:1-13, packages/remotes/src/lib/enhance.svelte.ts:1-5
Core Principles
The validation user experience is guided by two main principles:
- Issues Appear on
blur: Validation errors for a specific field are only displayed after the user navigates away from that field. This gives them a chance to complete their input without premature interruption. - Issues Clear on
input: As soon as the user begins to modify a field that has an issue, the issue is cleared. This provides immediate feedback that the system is responding to their attempt to correct the data.
This logic ensures that the UI feels responsive and helpful, rather than punitive. It is inspired by the validation behavior of libraries like superforms.
Sources: README.md:6-8, packages/remotes/src/lib/validation.svelte.ts:161-187
The createValidation Helper
The createValidation function is the central component for implementing the validation UX. It is initialized with a RemoteForm instance and returns an object with methods and properties to manage validation state and bind to form inputs.
Sources: packages/remotes/src/lib/validation.svelte.ts:13-15, packages/remotes/src/lib/index.ts:1
API Surface
The createValidation function returns an object with several methods to manage and display validation issues.
| Method/Property | Description |
|---|---|
fields(path) | Returns onblur and oninput event handlers for a given field path. This is the core function for binding the UX logic to an input. |
issues(path) | Returns a reactive array of issue strings for a field path, or null if there are no issues. |
allIssues() | Returns a reactive object containing all current validation issues for the form. |
addIssue(path, issue) | Manually adds a custom validation error to a specific field. Useful for client-side-only validation logic. |
reset() | Clears all validation issues from the state. |
validateAll() | Triggers validation for all registered fields against the server. |
updateIssues() | Populates the local issue state from the RemoteForm's issues, typically after a server round-trip. |
Sources: README.md:14-22, packages/remotes/src/lib/validation.svelte.ts:200-208, packages/remotes/CHANGELOG.md:7
Internal State and Data Structure
Validation state is managed within a reactive $state object. The structure is a nested object that mirrors the shape of the form data, where each leaf property holds an array of error message strings or null.
type ValidationIssues = {
[key: string]: string[] | null | ValidationIssues
}
let issues = $state<ValidationIssues>({})
Sources: packages/remotes/src/lib/validation.svelte.ts:4-8
The helper uses utility functions getNestedValue and setNestedValue to interact with this state object using dot-notation paths (e.g., 'address.state').
Sources: packages/remotes/src/lib/validation.svelte.ts:18-42, packages/remotes/src/lib/validation.svelte.ts:47-56
Event Handling Logic
The fields(path) method is the key to the UX philosophy. It returns onblur and oninput handlers that orchestrate the validation flow.
The diagram below illustrates the logic within the onblur and oninput event handlers returned by fields(path).
onblur: When the user leaves a field, a debounced remote validation is triggered. The local state is updated with any resulting issues. It includes a fallback to do a full server validation if the initial preflight check returns no issues.oninput: When the user types, if the field currently has issues, a lightweightpreflightOnlyvalidation is run. If the field becomes valid, the issues are cleared from the state, providing immediate feedback. A debounced remote validation is also used here if issues persist.
The debouncing is handled by runed's useDebounce utility to prevent excessive network requests while the user is interacting with the form.
Sources: packages/remotes/src/lib/validation.svelte.ts:161-187, packages/remotes/src/lib/validation.svelte.ts:88-94, packages/remotes/package.json:14
Integration with createEnhancedForm
While createValidation can be used standalone, it is designed to integrate with createEnhancedForm for a more streamlined experience. createEnhancedForm accepts a validation instance in its options.
// packages/remotes/src/lib/enhance.svelte.ts:40-45
type CreateEnhancedFormOptions = {
/** Optional validation instance from createValidation for form validation integration */
validation?: Validation
/** Milliseconds to wait before transitioning to 'delayed' state */
delayMs?: number
/** Milliseconds to wait before transitioning to 'timeout' state */
timeoutMs?: number
}
When integrated, createEnhancedForm automatically calls the appropriate createValidation methods during the form submission lifecycle:
validation.updateIssues()is called when the server returns validation issues (onIssuesstate).validation.validateAll()is called when the submission results in an error (onErrorstate).
This automation removes boilerplate code and ensures that the validation state is always synchronized with the server's response after a submission.
Sources: packages/remotes/src/lib/enhance.svelte.ts:42, packages/remotes/src/lib/enhance.svelte.ts:269-271, packages/remotes/src/lib/enhance.svelte.ts:277-279, README.md:50-54
Form Submission Flow with Validation
The following sequence diagram shows how createEnhancedForm and createValidation work together during a form submission that fails due to validation errors.
Conclusion
The validation UX philosophy in @opensky/remotes prioritizes a smooth and intuitive user experience by carefully controlling when validation feedback is presented. By showing errors on blur and clearing them on input, it avoids distracting the user while they are actively providing input. The createValidation helper provides the core logic, and its seamless integration with createEnhancedForm automates state synchronization, resulting in a powerful and developer-friendly system for handling form validation in SvelteKit applications.