Home
Relevant source files
The following files were used as context for generating this wiki page:The @opensky/remotes package provides helper utilities designed to improve the developer and user experience when working with SvelteKit's remote form functions. It offers two primary helpers, createValidation and createEnhancedForm, which can be used together or independently to add sophisticated validation logic and form submission state management.
This package aims to simplify common form-related patterns, such as displaying validation messages, managing loading states, and handling optimistic updates, drawing inspiration from libraries like superforms. The helpers are exported from the main package entry point.
Sources: README.md, packages/remotes/package.json:3, packages/remotes/src/lib/index.ts:1-2
createValidation
This helper is used to achieve a better user experience for form field validation. The core logic is to show validation issues only when a user leaves a field (onblur) and to clear them as soon as the user starts typing in that field again (oninput). This prevents validation errors from appearing distractingly during input.
Sources: README.md:5-7, packages/remotes/src/lib/validation.svelte.ts:10-12
API and Usage
The createValidation function takes a SvelteKit RemoteForm object as its only argument and returns an object with methods to manage validation.
Sources: packages/remotes/src/lib/validation.svelte.ts:14, README.md:12
| Method | Description |
|---|---|
fields(path: string) | Returns onblur and oninput event handlers for a given field path (e.g., 'name' or 'address.state'). |
issues(path: string) | Returns an array of validation issue strings for a field path, or null if there are no issues. |
allIssues() | Returns an 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 against the server. |
updateIssues() | Updates the local issue state for all registered fields based on the current state of the RemoteForm object. |
Sources: README.md:16-22, packages/remotes/src/lib/validation.svelte.ts:149-157
To use it, you spread the handlers from valid.fields('path') onto an input and use valid.issues('path') to conditionally display errors.
<script lang="ts">
import { createValidation } from '@opensky/remotes';
import { myForm } from './myForm.remote';
const valid = createValidation(myForm);
</script>
<input
{...myForm.fields.address.state.as('text')}
{...valid.fields('address.state')}
class:border-red-500={valid.issues('address.state')}
/>
{#if valid.issues('address.state')}
{#each valid.issues('address.state') as issue}
<p>{issue}</p>
{/each}
{/if}
Sources: README.md:31-43
Validation Flow
The fields method provides oninput and onblur handlers that implement the validation logic. The onblur event triggers a debounced remote validation.
This diagram illustrates the validation logic for a single field.
Implementation Details
- Debouncing: To avoid excessive server requests,
onblurvalidation calls are debounced usinguseDebouncefrom therunedlibrary. The default duration is 500ms. - Preflight Validation: The
oninputhandler usesform.validate({ preflightOnly: true })to quickly check for client-side schema violations (e.g., with Zod) without hitting the server, clearing issues as the user corrects them. - Path-based Access: The helper uses dot-notation strings (e.g.,
'address.state') to access nested fields and their corresponding issues, which it manages in an internal nested object.
Sources: packages/remotes/src/lib/validation.svelte.ts:17-45, 75-81
createEnhancedForm
This helper creates a reactive state machine for a remote form, providing granular control over the submission lifecycle through semantic callbacks. It tracks the form's state and exposes boolean getters for easily managing UI elements like buttons and loaders. It can also integrate with createValidation to automate validation updates.
Sources: README.md:58-63, packages/remotes/src/lib/enhance.svelte.ts:199-204
API and Usage
The function is overloaded to provide type-safe state and callbacks based on the options provided at creation.
Creation Options
The createEnhancedForm function accepts an optional options object.
| Option | Type | Description |
|---|---|---|
validation | Validation | An instance returned from createValidation for automatic issue handling. |
delayMs | number | Milliseconds to wait before the form state transitions to 'delayed'. Enables the delayed state and onDelay callback. |
timeoutMs | number | Milliseconds to wait before the form state transitions to 'timeout'. Enables the timeout state and onTimeout callback. |
Sources: packages/remotes/src/lib/enhance.svelte.ts:44-52, commit-message.txt:3-11
Returned API
| Property/Method | Description |
|---|---|
enhance(params, callbacks) | The form enhancement handler to be used with SvelteKit's form.enhance(). |
reset() | A method to manually reset the form's state back to 'idle'. |
state | A reactive string representing the current state of the form (e.g., 'idle', 'pending'). |
idle, pending, issues, error, result | Reactive boolean getters for the form's state. |
delayed | A reactive boolean getter, only available if delayMs was provided. |
timeout | A reactive boolean getter, only available if timeoutMs was provided. |
Sources: README.md:67-73, packages/remotes/src/lib/enhance.svelte.ts:120-197, CHANGELOG.md:5
Form State Machine
The createEnhancedForm helper manages the form's state through a defined lifecycle. The delayed and timeout states are conditional and only exist if delayMs and timeoutMs are configured.
This diagram shows the possible state transitions during a form submission.
Callbacks
The enhance method accepts a callbacks object to hook into the submission lifecycle.
| Callback | Context | Description |
|---|---|---|
onSubmit | { cancel, updates, data, ... } | Called before submission. Can be used to cancel the submission with cancel() or provide optimistic updates with updates(). |
onDelay | { data, ... } | Called if the submission takes longer than delayMs. Only available if delayMs is set. |
onTimeout | { data, ... } | Called if the submission takes longer than timeoutMs. Only available if timeoutMs is set. |
onReturn | { result, ... } | Called on a successful submission that returns a result. |
onIssues | { ... } | Called when the submission returns with validation issues. |
onError | { error, ... } | Called when the submission throws an error. |
Sources: README.md:76-87, packages/remotes/src/lib/enhance.svelte.ts:90-118
Enhance Handler Lifecycle
This sequence diagram illustrates the flow of control within the enhance handler.
Full Example
Here is an example demonstrating how createValidation and createEnhancedForm work together.
<script lang="ts">
import { createValidation, createEnhancedForm } from '@opensky/remotes'
import { myForm } from './myForm.remote'
import { z } from 'zod'
const schema = z.object({
name: z.string().min(4, 'Too short').max(10, 'Too long'),
address: z.object({
state: z.string()
})
})
const valid = createValidation(myForm)
const form = createEnhancedForm(myForm, {
validation: valid,
delayMs: 500,
timeoutMs: 3500
})
</script>
<p>State: {form.state}</p>
<form {...myForm.preflight(schema).enhance(opts =>
form.enhance(opts, {
onDelay: () => console.log('showing loader'),
onTimeout: () => console.log('request timeout'),
onReturn: ({ result }) => console.log('success', result)
})
)}>
<input
{...myForm.fields.name.as('text')}
{...valid.fields('name')}
class:error={valid.issues('name')}
/>
{#if valid.issues('name')}
{#each valid.issues('name') as issue}
<p class="error">{issue}</p>
{/each}
{/if}
<button disabled={form.pending || form.delayed}>
{form.delayed ? 'Loading...' : 'Submit'}
</button>
</form>
{#if form.timeout}
<p>Request timed out</p>
{/if}
Sources: README.md:99-148