Managing Validation Issues
Relevant source files
The following files were used as context for generating this wiki page:The @opensky/remotes package provides a createValidation helper to improve the user experience when working with validation issues in SvelteKit's remote forms. It introduces a specific logic for displaying and clearing validation messages: issues appear when a user leaves a field (onblur) but are cleared as soon as the user starts typing again (oninput). This prevents validation errors from appearing mid-typing, offering a smoother interaction.
This system is designed to work with SvelteKit's RemoteForm and can be integrated with the createEnhancedForm helper for more advanced state management and automated validation updates during the form submission lifecycle. The validation helper manages a reactive state of all validation issues, provides event handlers to bind to form fields, and includes methods for manual control over the validation state.
Sources: packages/remotes/README.md:3-8, packages/remotes/src/lib/validation.svelte.ts:10-14
Core Component: createValidation
The primary export for managing validation is the createValidation function. It takes a SvelteKit RemoteForm object as its only argument and returns a set of utilities for handling form validation.
Sources: packages/remotes/src/lib/index.ts:1, packages/remotes/src/lib/validation.svelte.ts:10-14
Initialization
To use the validation helper, import createValidation and pass your remoteForm instance to it.
<script lang="ts">
import { createValidation } from '@opensky/remotes';
import { myForm } from './myForm.remote';
const valid = createValidation(myForm);
</script>
Sources: packages/remotes/README.md:12-14
API and Usage
The createValidation function returns an object with several methods and properties to interact with the validation state.
| Method | Description |
|---|---|
fields(path: string) | Returns onblur and oninput event handlers for a given field path (e.g., 'name', 'address.state'). |
issues(path: string) | Returns a reactive array of validation issue messages for a field path, or null if there are no issues. |
allIssues() | Returns the entire reactive object containing all current validation issues for all registered fields. |
addIssue(path: string, issue: string) | Manually adds a custom validation error message to a specific field. |
reset() | Clears all validation issues from the state. |
validateAll() | Triggers validation for all registered fields, including making a call to the server. |
updateIssues() | Updates the internal validation state by re-populating it from the remoteForm's current issues. |
Sources: packages/remotes/README.md:16-23, packages/remotes/src/lib/validation.svelte.ts:153-162
Internal State Management
The helper maintains an internal reactive state object for validation issues using Svelte 5 Runes ($state). This object, issues, maps field paths to an array of error messages.
The structure of the ValidationIssues type allows for nested objects, mirroring the structure of the form data.
type ValidationIssues = {
[key: string]: string[] | null | ValidationIssues
}
Sources: packages/remotes/src/lib/validation.svelte.ts:4-6, packages/remotes/src/lib/validation.svelte.ts:15
Internal functions setNestedValue and getNestedValue are used to interact with this state object using dot-notation paths (e.g., 'address.city').
Sources: packages/remotes/src/lib/validation.svelte.ts:20-50
Validation Lifecycle and Event Handling
The validation logic is primarily driven by the onblur and oninput event handlers returned by the fields(path) method.
Field Registration and Event Binding
When you call valid.fields('path.to.field'), the path is registered for tracking. This call returns the necessary event handlers to spread onto your input element.
<input
{...myForm.fields.name.as('text')}
{...valid.fields('name')}
class:error={valid.issues('name')}
/>
Sources: packages/remotes/README.md:123-127, packages/remotes/src/lib/validation.svelte.ts:117-119
The diagram below shows how a field path is registered and used to retrieve issues.
onblur Event Flow
When the user leaves a field, the onblur handler triggers a debounced remote validation call. This avoids excessive server requests while still providing server-side validation.
This diagram illustrates the onblur validation process.
oninput Event Flow
When the user types in a field that has an error, the oninput handler triggers a client-side-only (preflightOnly) validation to clear the error as soon as the input becomes valid. If client-side validation passes, it may trigger a debounced remote validation to confirm.
This diagram shows the oninput logic for clearing issues.
Debounced Remote Validation
To prevent sending a validation request to the server on every keystroke, createValidation uses a debounced function remoteValidate. This function, created with useDebounce from the runed library, waits for a default of 500ms of inactivity before calling form.validate().
Sources: packages/remotes/src/lib/validation.svelte.ts:64-70, packages/remotes/package.json:14
Integration with createEnhancedForm
For a more robust solution, createValidation is designed to integrate with createEnhancedForm. By passing the validation instance during the creation of the enhanced form, you automate the process of updating validation issues after a form submission.
const valid = createValidation(myForm);
const form = createEnhancedForm(myForm, {
validation: valid, // Pass the instance here
delayMs: 500,
timeoutMs: 3500
});
Sources: packages/remotes/README.md:116-121, packages/remotes/src/lib/enhance.svelte.ts:200-204
When integrated, createEnhancedForm automatically calls:
validation.updateIssues()when the server returns validation issues (onIssueslifecycle).validation.validateAll()when the submission results in an error (onErrorlifecycle).
This ensures that server-side validation messages returned upon form submission are correctly displayed on their respective fields.
This diagram shows the automated validation calls within the createEnhancedForm lifecycle.
Summary
The createValidation helper provides a refined approach to handling form validation in SvelteKit. By controlling when validation messages are shown and cleared, it offers a less intrusive and more intuitive user experience. Its ability to handle nested field structures, perform debounced remote validation, and seamlessly integrate with createEnhancedForm makes it a powerful utility for complex forms. This system abstracts away the boilerplate of managing reactive error states and synchronizing them with SvelteKit's remote form lifecycle.
Sources: packages/remotes/README.md:3-8, packages/remotes/CHANGELOG.md:28-30