Migration from v2
Before migrating, make sure you are using the latest version of SJSF and that you are not using any deprecated APIs.
Update your defaults.ts file:
import { createFormValidator } from "@sjsf/ajv8-validator";export const validator = createFormValidator();export { createFormValidator as validator } from "@sjsf/ajv8-validator";export { createFormIdBuilder as idBuilder } from "@sjsf/form/id-builders/legacy";export { createFormMerger as merger } from "@sjsf/form/mergers/legacy";This change should be sufficient for migrating simple projects. However, I recommend reviewing the full list of changes, as you might find opportunities to improve or simplify your current solution.
Form options overhaul
Section titled “Form options overhaul”- Introduced a new
idBuilderoption, replacing the formeridSeparatorandidPseudoSeparatorproperties - The
mergeroption is now required - The
idBuilder,merger, andvalidatoroptions can now also be provided as factory functions
This change addresses several issues:
- A single validator instance was shared across all forms
- Form parameters had to be passed manually to validator and merger (
uiSchema,idPrefix, etc.) - The default merger relied on a heavy library as a dependency
- Uncontrolled ID generation system
Now:
- Each form creates its own validator and merger instances, passing the necessary parameters automatically
- The merger is specified explicitly, making it easy to replace if needed
- A new merger is available with significant improvements in both performance and bundle size
(
@sjsf/form/mergers/modern) - A controlled and configurable ID generation system
All form options now can be reactive
Section titled “All form options now can be reactive”All form options now support reactivity. For example:
import { createForm } from '@sjsf/form';
import { getInitialData } from './data.remote';
const initialData = $derived(await getInitialData())
const form = createForm({ get initialValue() { return initialData; }, get initialErrors() { initialData; return undefined; } /// ...})When initialData changes, the form state will be updated!
Changed default values population behavior
Section titled “Changed default values population behavior”Previously, objects and tuples were always initialized with a value, even when they were not required. For example:
{ "type": "object", "properties": { "foo": { "type": "object", "properties": { "bar": { "type": "string" } }, "required": ["bar"] } }}The foo object used to be initialized as an empty object (unless another initial value was provided).
This forced users to fill in the bar field, even though the foo object itself was optional.
Now, when creating a form, the form state is initialized with an empty object (for this case),
and the form can be submitted without filling in the bar field.
The validator form option now requires FormValidator<T>
Section titled “The validator form option now requires FormValidator<T>”This means that, in addition to the isValid method, an implementation of the form value validation method is now required.
You can use the noop validator from the @sjsf/form/validators/noop module to skip validation.
The form.context property has been removed
Section titled “The form.context property has been removed”Previously, the public and internal APIs were split between two objects:
form and form.context.
In v3, form.context and FormInternalContext no longer exist.
All internal fields and methods have been moved directly into form,
but they must be accessed via internal symbols.
The form.value property has been removed
Section titled “The form.value property has been removed”The functionality of this property has now been split across several methods:
- Use the
setValuemethod to set a new form value. - Use the
getValueSnapshotmethod to retrieve a snapshot of the internal form state. - Use the
validate/validateAsyncmethod to retrieve a typed value.
The form.errors property has been removed
Section titled “The form.errors property has been removed”Reason — to hide the internal error storage structure from the user for future optimization.
You can access form errors and even more by using the following functions:
hasErrors, getErrors, updateErrors, getFieldErrors, updateFieldErrors
The getSnapshot form option has been removed
Section titled “The getSnapshot form option has been removed”To apply transformations before validation, you now need to augment the validator - see State transformation.
onsubmit and onreset attributes
Section titled “onsubmit and onreset attributes”The onsubmit and onreset attributes applied to Form, BasicForm,
or via uiSchema override the default handlers.
If you need additional handlers, use attachments
or call form.submit(e) in your handler.
Custom components
Section titled “Custom components”ArrayContext and ObjectContext objects update
Section titled “ArrayContext and ObjectContext objects update”Context constructors now accept an options object instead of a list of arguments:
const objCtx = createObjectContext({ ctx, config: () => config, value: () => value, translate,});These interfaces now only include methods, making them easier to extend:
const myObjCtx = { ...objCtx, canExpand() => someCondition && objCtx.canExpand(),}setObjectContext(myObjCtx)Forced support for nullable fields
Section titled “Forced support for nullable fields”The FieldCommonProps interface has been updated:
export interface FieldCommonProps<V> { ... value: V | undefined; value: V | null | undefined;}This change introduces explicit support for nullable fields (example).
In most cases, it’s enough to update your widget’s value binding from:
bind:value to bind:value={() => value ?? undefined, (v) => (value = v)}
Using attachment for attaching event handlers
Section titled “Using attachment for attaching event handlers”Previously, helper functions like inputProps, selectProps, and textareaProps
passed event handlers as attributes, which in some cases could be overwritten by the user.
Now, instead of attributes, they use attachment,
where handlers are added via addEventListener.
If your component does not support attachment,
or attaches handlers to the wrong DOM node,
you should map the attributes manually:
inputAttributes(ctx, config, "text", handlers, { // spread handlers as attributes ...handlers, type: "text",});Field component update
Section titled “Field component update”The name prop has been replaced with path.
Other changes
Section titled “Other changes”fields/extra-fields/*submodule now deprecated, migrate tofields/extra/*- The
makeEventHandlersfunction signature has been changed - The
isSubmittedandisChangedproperties are nowreadonly isSubmittedis reset after successful processing (validation +onSubmithandler)isChangedbecomestrueafter any interaction until the form is successfully submitted or reset- The
idSeparatorandidPseudoSeparatorform parameters moved toidBuilder - Removed redundant types:
IdPrefixOptionIdSeparatorOptionIdPseudoSeparatorIdOptionsPathToIdOptions
focus-on-first-errormodule changes:- Updated signatures of
getErrorsListandgetFocusAction - Removed the
GetFocusActionOptionstype
- Updated signatures of
- Replace
idproperty withpathinConfigtype - Replace
idFromPathwithcreateIdfunction - Replace
createChildIdwithcreateChildPathfunction - Replace
getErrorswithgetFieldErrorsfunction
Validators
Section titled “Validators”Zod and Valibot
Section titled “Zod and Valibot”The setupFormValidator and setupAsyncFormValidator functions have been renamed
to adapt and adaptAsync.
They can now be used directly when calling createForm:
const form = createForm({ ...defaults, ...adapt(schema),});Ajv and @exodus/schemasafe
Section titled “Ajv and @exodus/schemasafe”In the precompile submodules, the createFormValidator functions
have been replaced with createFormValidatorFactory.
For convenient use with createForm:
const form = createForm({ ...defaults, schema, validator: createFormValidatorFactory({ validateFunctions }), });Standard schema
Section titled “Standard schema”The signatures of createFormValueValidator and createAsyncFormValueValidator
from the @sjsf/form/validators/standard-schema module have changed.
Now, the Standard Schema is passed as the first argument, followed by the remaining options.
Themes
Section titled “Themes”daisyUI v5
Section titled “daisyUI v5”File renames:
@sjsf/daisyui5-theme/extra-widgets/pikaday-date-picker.svelte→@sjsf/daisyui5-theme/extra-widgets/date-picker.svelte@sjsf/daisyui5-theme/extra-widgets/pikaday-date-picker-include→@sjsf/daisyui5-theme/extra-widgets/date-picker-include
Widget changes:
@sjsf/daisyui5-theme/extra-widgets/cally-date-picker-includenow includesdaisyui5CallyDatePickerWidgetinstead ofdatePickerWidget@sjsf/daisyui5-theme/extra-widgets/filter-radio-buttons-includenow includesdaisyui5FilterRadioButtonsWidgetinstead ofradioButtonsWidget
Flowbite
Section titled “Flowbite”The radioButtons widget now uses RadioButton instead of Button Toggle component.
To preserve the previous behavior you can use the following import,
witch includes flowbite3ToggleRadioButtonsWidget widget:
import "@sjsf/flowbite3-theme/extra-widgets/toggle-radio-buttons-include";Skeleton v3
Section titled “Skeleton v3”Widget changes:
@sjsf/skeleton3-theme/extra-widgets/file-upload-includenow includesskeleton3FileUploadWidgetinstead offileWidget@sjsf/skeleton3-theme/extra-widgets/slider-includenow includesskeleton3SliderWidgetinstead ofrangeWidget
shadcn-svelte
Section titled “shadcn-svelte”The theme now requires the Field* and ButtonGroup components to function properly.
SvelteKit
Section titled “SvelteKit”The makeFormDataParser and validateForm functions
have been replaced with createFormHandler.
See the integration page for more information.
The initForm function has been removed — specify initial values directly instead.
import type { InitialFormData } from "@sjsf/sveltekit";
export const load = async () => { return { form: { schema, initialValue: { ... }, } satisfies InitialFormData, };};Dependency versions have been updated
Section titled “Dependency versions have been updated”svelte@^5.43.0@sveltejs/kit@^2.48.3(@sjsf/sveltekit)daisyui@^5.3.0(@sjsf/daisyui5-theme)flowbite-svelte-icons@^3.0.0(@sjsf/flowbite-icons)flowbite-svelte@^1.19.0(@sjsf/flowbite3-theme)bits-ui@^2.14.0(@sjsf/shadcn4-theme)- New peer dependency
@jis3r/icons@^1.1.0(@sjsf/moving-icons) zod@^4.1.0(@sjsf/zod4-validator)
Deprecated packages were removed
Section titled “Deprecated packages were removed”@sjsf/zod-validator(v3)@sjsf/skeleton-theme(tw3)
Feel free to get in touch about migrating to v3 on GitHub.