By default, form data when submitted will be validated using HTML5 validation and the provided validator.
Live validation (not recommended)
You can easily implement live validation by utilizing Svelte 5 reactivity
import { useForm2, SimpleForm, type Schema } from " @sjsf/form " ;
import { translation } from " @sjsf/form/translations/en " ;
import { theme } from " @sjsf/form/basic-theme " ;
import { createValidator } from " @sjsf/ajv8-validator " ;
const validator = createValidator ();
form . errors = form . validate ();
< SimpleForm { form } style = " display: flex; flex-direction: column; gap: 1rem " />
While it is possible, this approach has low efficiency because
it is usually not meaningful to validate the entire form when only one field is changed.
Fields validation
Instead of performing a full form validation every time a field is changed,
we propose to validate only the field being changed and
full validation of the form on submission.
Note
By default, fields validation is performed with a debounce of 300ms.
This behavior can be changed using the fieldsValidationDebounceMs
option.
import { ON_CHANGE, ON_INPUT, AFTER_SUBMITTED, SimpleForm } from " @sjsf/form " ;
import { useCustomForm } from " @/components/custom-form " ;
import { objectSchema } from " ./_demo-schemas " ;
const form = useCustomForm ( {
fieldsValidationMode: ON_INPUT | ON_CHANGE | AFTER_SUBMITTED ,
style = " display: flex; flex-direction: column; gap: 1rem "
The form in this example will validate fields during the input
and change
events only after the first submission of the form.
Async validation
The form supports asynchronous validation.
To correctly display potential validation process errors, the handleValidationProcessError
option should be used.
import Ajv, { type AsyncSchema, type SchemaValidateFunction } from " ajv " ;
import { addFormComponents, createAsyncValidator } from " @sjsf/ajv8-validator " ;
import { ON_INPUT, SimpleForm } from " @sjsf/form " ;
import { handleValidationProcessError } from ' @sjsf/form/translations/en ' ;
import { Status } from ' @sjsf/form/use-mutation.svelte ' ;
import { useCustomForm } from " @/components/custom-form " ;
const ajv = addFormComponents ( new Ajv ());
const validate : SchemaValidateFunction = async ( schema , data ) => {
await new Promise ( ( resolve ) => setTimeout (resolve , 1000 )) ;
if (Math . random () > 0.5 ) {
throw new Error ( " async error " ) ;
return data . length >= schema . minimum && data . length <= schema . maximum ;
const schema : AsyncSchema = {
const form = useCustomForm ( {
validator: createAsyncValidator ( { ajv } ) ,
handleValidationProcessError ,
fieldsValidationMode: ON_INPUT ,
const statusNames : Record < Status , string > = {
[Status . Processed ] : " processed " ,
[Status . Success ] : " success " ,
[Status . Failed ] : " failed " ,
form validation: { statusNames[form . validation . status ] } , fields validation: { statusNames[form . fieldsValidation . status ] }
style = " display: flex; flex-direction: column; gap: 1rem "
Please see your validator page for more information.
Focus on first error
You can achieve focus on the first error by using the focusOnFirstError
function.
import { SimpleForm } from " @sjsf/form " ;
import { focusOnFirstError } from " @sjsf/form/focus-on-first-error " ;
import { useCustomForm } from " @/components/custom-form " ;
import { objectSchema } from " ./_demo-schemas " ;
const form = useCustomForm ( {
onSubmitError: focusOnFirstError ,
style = " display: flex; flex-direction: column; gap: 1rem "
focusOnFirstError
will try to find a focusable element and focus it.
If it’s not found, it will try to find an errors list and scroll it into view.
If it’s not found, it will return false
, so you can extend the behavior.
Errors list
If necessary, you can create a list of errors
import { SimpleForm } from " @sjsf/form " ;
import { useCustomForm } from " @/components/custom-form " ;
import { objectSchema } from " ./_demo-schemas " ;
const form = useCustomForm ( {
style = " display: flex; flex-direction: column; gap: 1rem "
{# if form . errors . size > 0 }
< div style = " padding-top: 1rem; " >
< span style = " font-size: larger; font-weight: bold; " > Errors </ span >
{# each form . errors as [field, fieldErrors] (field)}
{# each fieldErrors as err}
< li > " { err . propertyTitle } " { err . message } </ li >