Skip to content
Playground Form Builder

Validation

By default, form data when submitted will be validated using HTML5 validation and the provided validator.

You can easily implement live validation by utilizing Svelte 5 reactivity

<script lang="ts">
import { untrack } from "svelte";
import {
createForm,
BasicForm,
hasFieldStateByPath,
type Schema,
FIELD_INTERACTED,
updateErrors,
validate,
} from "@sjsf/form";
import * as defaults from "@/lib/form/defaults";
const schema: Schema = {
title: "Live validation",
properties: {
foo: {
type: "string",
minLength: 10,
},
bar: {
type: "number",
minimum: 1000,
},
},
};
const form = createForm({
...defaults,
initialValue: {
foo: "initial",
bar: 1,
},
schema,
onSubmit: console.log,
});
$effect(() => {
// NOTE: `validate()` reads the state snapshot,
// causing `$effect` to subscribe to all changes.
const { errors = [] } = validate(form);
updateErrors(
form,
untrack(() =>
errors.filter((e) =>
hasFieldStateByPath(form, e.path, FIELD_INTERACTED)
)
)
);
});
</script>
<BasicForm {form} />

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.

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.

<script lang="ts">
import { ON_CHANGE, ON_INPUT, SimpleForm } from "@sjsf/form";
import * as defaults from "@/lib/form/defaults";
import { objectSchema, objectUiSchema } from "./demo-schemas";
</script>
<SimpleForm
{...defaults}
schema={objectSchema}
uiSchema={objectUiSchema}
fieldsValidationMode={ON_INPUT | ON_CHANGE}
/>

The form in the example above, will validate fields on input and change events.

The form supports asynchronous validation. By default:

  • a new form validation process can only be started after the previous one is completed;
  • a new fields validation process aborts the previous one;
  • fields validation abort errors are not displayed.

Async form

Please see your validator page for more information.

You can achieve focus on the first error by using the createFocusOnFirstError function.

<script lang="ts">
import { SimpleForm } from "@sjsf/form";
import { createFocusOnFirstError } from "@sjsf/form/focus-on-first-error";
import * as defaults from "@/lib/form/defaults";
import { objectSchema, objectUiSchema } from "./demo-schemas";
</script>
<SimpleForm
{...defaults}
schema={objectSchema}
uiSchema={objectUiSchema}
onSubmitError={createFocusOnFirstError()}
/>

  1. focusOnFirstError will try to find a focusable element and focus it.
  2. If it’s not found, it will try to find an errors list and scroll it into view.
  3. If it’s not found, it will return false, so you can extend the behavior.

If necessary, you can create a list of errors

<script lang="ts">
import { BasicForm, createForm, getErrors, hasErrors } from "@sjsf/form";
import * as defaults from "@/lib/form/defaults";
import { objectSchema } from "./demo-schemas";
const form = createForm({
...defaults,
schema: objectSchema,
});
</script>
<BasicForm {form} novalidate />
{#if hasErrors(form)}
<div style="padding-top: 1rem;">
<span style="font-size: larger; font-weight: bold;">Errors</span>
<ui style="color: red;">
{#each getErrors(form) as [path, errors] (path)}
{#each errors as error}
<li>{error}</li>
{/each}
{/each}
</ui>
</div>
{/if}

In some cases it may be necessary to transform the form state before it is passed to the validator.

You can do this by extending the validator.

One of the transformation options you can apply is deleting unnecessary data.

For this you can use omitExtraData function.

import { createForm, type FormValueValidator } from "@sjsf/form";
import { omitExtraData } from "@sjsf/form/omit-extra-data";
import * as defaults from '@/lib/form/defaults'
import { schema, type FormValue } from "./schema";
const form = createForm<FormValue>({
...defaults,
validator: (options) => {
const v = defaults.validator<FormValue>(options)
return {
...v,
validateFormValue(rootSchema, formValue) {
const cleanData = omitExtraData(
v,
options.merger(),
options.schema,
formValue
);
return v.validateFormValue(rootSchema, cleanData);
},
} satisfies FormValueValidator<FormValue>;
},
schema,
onSubmit(value) {
console.log("transformed", value);
},
onSubmitError({ value, errors }) {
console.log("transformed", value);
console.log("errors", errors);
}
})
// Obtaining a value with the applied transformation
const { value } = validate(form)

You can also use the withOmitExtraData(defaults.validator) function from the @sjsf/form/validators/omit-extra-data module to achieve the same effect.