5.x Upgrade Guide
Breaking changes
There were several significant breaking changes in RJSF version 5 that were necessary in order to support the following new features:
- Schema validation was decoupled from
@rjsf/core
to resolve issue #2693. Additionally, in order to break a circular dependency in the validation refactor, the@rjsf/core/utils.js
file was split out into its own@rjsf/utils
package as was suggested in #1655. - The theme for Material UI version 5 (i.e.
@rjsf/mui
) was split out of the theme for version 4 (i.e.@rjsf/material-ui
) to resolve the following issues: #2762, #2858, #2905, #2945 - As part of the fix for #2526 all the existing templates in the previous version were moved into a new
templates
dictionary, similar to howwidgets
andfields
work. Thistemplates
dictionary was added to theRegistry
and also theForm
props, replacing theArrayFieldTemplate
,FieldTemplate
,ObjectFieldTemplate
andErrorList
props. In addition, several of thefields
andwidgets
based components were moved into thetemplates
dictionary as they were more like templates than trueField
s orWidget
s. Also fixes #2945 - Fixed
anyOf
andoneOf
getting incorrect, potentially duplicate ids when combined with array #2197 CheckboxesWidget
,RadioWidget
andSelectWidget
in all themes now use indexes intoenumOptions[]
rather thanoption.value
to allow for objects inenumOptions
- #1494
Node support
Version 5 is dropping official support for Node 12 as it is no longer a maintained version.
Please use Node 18 when making any changes to package.json
and package-lock.json
files.
All PR and branch builds are running against Node 14, 16 and 18.
React version
RJSF is no longer actively supporting React version < 16.14.x. React 17 is officially supported on all the themes where the underlying theme library also supports React 17.
Unfortunately, there is required work pending to properly support React 18, so use it at your own risk.
New packages
There are four new packages added in RJSF version 5:
@rjsf/utils
: All of the utility functions previously imported from@rjsf/core/utils
as well as the Typescript types for RJSF version 5.- The following new utility functions were added:
ariaDescribedByIds()
,createSchemaUtils()
,descriptionId()
,enumOptionsDeselectValue()
,enumOptionsIndexForValue()
,enumOptionsIsSelected()
,enumOptionsSelectValue()
,enumOptionsValueForIndex()
,errorId()
,examplesId()
,getClosestMatchingOption()
,getFirstMatchingOption()
,getInputProps()
,helpId()
,mergeValidationData()
,optionId()
,sanitizeDataForNewSchema()
andtitleId()
- The following new utility functions were added:
@rjsf/validator-ajv6
: The ajv-v6-based validator refactored out of@rjsf/core@4.x
, that implements theValidatorType
interface defined in@rjsf/utils
.@rjsf/validator-ajv8
: The ajv-v8-based validator that is an upgrade of the@rjsf/validator-ajv6
, that implements theValidatorType
interface defined in@rjsf/utils
. See the ajv 6 to 8 migration guide for more information.@rjsf/mui
: Previously@rjsf/material-ui/v5
, now provided as its own theme.
id
BREAKING CHANGES
In many of the themes the id
s for the Title
, Description
and Examples
blocks were update to all have a consistent value of xxx__title
, xxx__description
and xxx__examples
, respectively, where xxx
is the id of the field.
In addition, some of the id
s for various input values were updated to be consistent across themes or to fix small bugs.
For instance, the values for radio buttons in the RadioWidget
and checkboxes in the CheckboxesWidget
are of the form xxx-${option.value}
, where xxx
is the id of the field.
enumOptions[]
widgets BREAKING CHANGES
Schemas may have enumOptions[]
where values are objects rather than primitive types. Examples of such schemas have been added to the playground. These schemas did not work in RJSF prior to v5.
In every theme, the enumOptions[]
rendering widgets CheckboxesWidget
, RadioWidget
and SelectWidget
previously used the enumOptions[].value
to as the value used for the underlying checkbox
, radio
and select.option
elements.
Now, these CheckboxesWidget
, RadioWidget
and SelectWidget
components use the index of the enumOptions[]
in the list as the value for the underlying elements.
If you need to build a custom widget for this kind of enumOptions
, there are a set of enumOptionsXXX
functions in @rjsf/utils
to support your implementation.
@rjsf/core
BREAKING CHANGES
Types
In version 4, RJSF exported all its types directly from @rjsf/core
.
In version 5, only the types for the Form
component and the withTheme()
HOC are exported directly from @rjsf/core
.
All the rest of the types for RJSF are now exported from the new @rjsf/utils
package.
NOTE: The types in @rjsf/utils
have been improved significantly from those in version 4 and as a result may require you to fix your Typescript typings and add some casts.
Some of the most notable changes are:
RJSFSchema
has replaced the use ofJSON7Schema
for future compatibility reasons.- Currently
RJSFSchema
is simply an alias toJSON7Schema
so this change is purely a naming one. - It is highly recommended to update your use of
JSON7Schema
withRJSFSchema
so that when the RJSF begins supporting a newer JSON Schema version out-of-the-box, your code won't be affected.
- Currently
RJSFSchemaDefinition
has replaced the use ofJSONSchema7Definition
for the same reasons.The use of the generic
T
(defaulting toany
) for theformData
type has been expanded to cover all type hierarchies that useformData
.StrictRJSFSchema
andRJSFSchema
have replaced the use ofJSON7Schema
for future compatibility reasons.RJSFSchema
isStrictRJSFSchema
joined with theGenericObjectType
(i.e.{ [key: string]: any }
) to allow for additional syntax related to newer draft versions- All definitions of
schema
androotSchema
elements have been replaced with a generic that is defined asS extends StrictRJSFSchema = RJSFSchema
- It is highly recommended to update your use of
JSON7Schema
withRJSFSchema
since that is the default for the new generic used forschema
androotSchema
A new generic
F
(extendingFormContextType
defaulting toany
) was added for theformContext
type, and all types in the hierarchy that useformContext
have had that generic added to them.The new
CustomValidator
,ErrorTransformer
,ValidationData
,ValidatorType
andSchemaUtilsType
types were added to support the decoupling of the validation implementation.The new
TemplatesType
,ArrayFieldDescriptionProps
,ArrayFieldTitleProps
,UnsupportedFieldProps
,IconButtonProps
,SubmitButtonProps
andUIOptionsBaseType
were added to support the consolidation (and expansion) oftemplates
in theRegistry
andForm
.BREAKING CHANGE The
DescriptionField
andTitleField
props were removed from theArrayFieldTemplateProps
andObjectFieldTemplateProps
as they can now be derived from thetemplates
oruiSchema
via the newgetTemplate()
utility function.BREAKING CHANGE The
fields
prop was removed from theFieldTemplateProps
as you can simply useregistry.fields
instead.BREAKING CHANGE The
showErrorList
prop was changed to acceptfalse
,"top"
or"bottom"
.true
is no longer a valid value. The default value is"top"
, which has identical behavior to the default value/true
in v4. You can view all these types on GitHub.
Form props
In version 5, the Form
component's two optional props additionalMetaSchemas
and customFormats
were replaced with the new, required validator
prop, in order to support the decoupling of the validation implementation.
This new validator
prop is expected to be an implementation of the ValidatorType
interface.
The new @rjsf/validator-ajv6
package contains the refactored implementation of the version 4 validator; It was provided for backwards compatibility with RJSF v4, and it is deprecated.
The new @rjsf/validator-ajv8
package contains the refactored implementation of the version 4 validator, that has been converted to use the Ajv 8
validator and has more capabilities than the Ajv 6
one. See the Ajv migration guide for more information.
There are two ways to use this new package to provide a validator
for a Form
.
First, you can simply import the default validator from the package and pass it to a Form
.
import { RJSFSchema } from "@rjsf/utils";
import Form from "@rjsf/core";
import validator from "@rjsf/validator-ajv8";
// Your schema
const schema: RJSFSchema = { ... };
render((
<Form schema={schema} validator={validator} />
), document.getElementById("app"));
Second, if you were actually providing one (or both) of the removed optional props to your Form
, you can continue using them by creating a customized validator.
import { RJSFSchema } from "@rjsf/utils";
import Form from "@rjsf/core";
import { customizeValidator, CustomValidatorOptionsType } from "@rjsf/validator-ajv8";
// Your schema, additionalMetaSchemas and/or customFormats
const schema: RJSFSchema = { ... };
const additionalMetaSchemas: CustomValidatorOptionsType['additionalMetaSchemas'] = [{ ... }];
const customFormats: CustomValidatorOptionsType['customFormats'] = { ... };
const validator = customizeValidator({ additionalMetaSchemas, customFormats });
render((
<Form schema={schema} validator={validator} />
), document.getElementById("app"));
formElement
converted to RefObject
The formElement
variable that stored the ref to the inner <form />
was converted from a simple variable assigned via a callback ref (ala React < 16.3) to a React.RefObject created using the React.createRef()
API.
As a result, if you were using the formElement
ref, you will need to update it to use formElement.current
:
import { RJSFSchema } from "@rjsf/utils";
import Form from "@rjsf/core";
import validator from "@rjsf/validator-ajv8";
// Your schema
const schema: RJSFSchema = { ... };
const formRef = React.createRef();
render((
<Form ref={formRef} schema={schema} validator={validator} />
), document.getElementById("app"));
...
// Previously, to reset the form one would have called:
// formRef.current.formElement.reset();
// Now one calls:
formRef.current.formElement.current.reset();
validate
prop renamed
Additionally, in version 5, the validate
prop on Form
was renamed to customValidate
to avoid confusion with the new validator
prop.
fields
prop changes
In previous versions, it was possible to provide an override to the DescriptionField
, TitleField
and/or UnsupportedField
components by providing a custom implementation in the fields
prop on the Form
.
Since these components do not actually support the FieldProps
interface, they were moved into the templates
dictionary instead.
If you were previously overriding any (or all) of these components, you can override them now via the templates
prop on Form
instead:
import { DescriptionFieldProps, RJSFSchema, TitleFieldProps } from "@rjsf/utils";
import Form from "@rjsf/core";
import validator from "@rjsf/validator-ajv8";
// Your schema
const schema: RJSFSchema = { ... };
// Your custom fields
const CustomDescriptionField = (props: DescriptionFieldProps) => { ... };
const CustomTitleField = (props: TitleFieldProps) => { ... };
const CustomUnsupportedField = (props: ObjectFieldTemplateProps) => { ...
};
const templates: Partial<TemplatesType> = {
DescriptionFieldTemplate: CustomDescriptionField,
TitleFieldTemplate: CustomTitleField,
UnsupportedFieldTemplate: CustomUnsupportedField,
};
render((
<Form schema={schema} validator={validator} templates={templates}/>
), document.getElementById("app"));
new templates
prop
Additionally, in version 5, the ArrayFieldTemplate
, FieldTemplate
, ObjectFieldTemplate
and ErrorList
props were replaced with the templates
prop as part of the TemplatesType
consolidation.
If you were previously overriding any (or all) of these templates, you can simply consolidate them into the new templates
prop on Form
instead:
import { ArrayFieldTemplateProps, ErrorListProps, FieldTemplateProps, ObjectFieldTemplateProps, RJSFSchema } from "@rjsf/utils";
import Form from "@rjsf/core";
import validator from "@rjsf/validator-ajv8";
// Your schema
const schema: RJSFSchema = { ... };
// Your custom templates
const CustomArrayFieldTemplate = (props: ArrayFieldTemplateProps) => { ... };
const CustomFieldTemplate = (props: FieldTemplateProps) => { ... };
const CustomObjectFieldTemplate = (props: ObjectFieldTemplateProps) => { ... };
const CustomErrorField = (props: ErrorListProps) => { ... };
const templates: Partial<TemplatesType> = {
ArrayFieldTemplate: CustomArrayFieldTemplate,
FieldTemplate: CustomFieldTemplate,
ObjectFieldTemplate: CustomObjectFieldTemplate,
ErrorFieldTemplate: CustomErrorField,
};
render((
<Form schema={schema} validator={validator} templates={templates} />
), document.getElementById("app"));
NOTE: In version 5, the ArrayField
implementation was refactored to add 3 additional templates for presenting arrays along with the ArrayFieldTemplate
.
If you were updating the ArrayFieldTemplate
to modify just a subset of the UI, it may be easier for you to implement one of the other new templates instead.
See the Custom Templates documentation for more details.
widgets
prop change
In the previous version, it was possible to provide an override to the SubmitButton
component by providing a custom implementation in the widgets
prop on the Form
.
Since this component only requires a tiny fraction of the WidgetProps
interface, it was moved into the templates.ButtonTemplates
dictionary instead with its own, reduced set of props.
If you were previously overriding this component, you can override it now via the templates
prop on Form
instead:
import { RJSFSchema, SubmitButtonProps } from "@rjsf/utils";
import Form from "@rjsf/core";
import validator from "@rjsf/validator-ajv8";
// Your schema
const schema: RJSFSchema = { ... };
// Your custom button
const CustomSubmitButton = (props: SubmitButtonProps) => { ...
};
const templates: Partial<TemplatesType> = {
ButtonTemplates: {
SubmitButton: CustomSubmitButton,
}
};
render((
<Form schema={schema} validator={validator} templates={templates}/>
), document.getElementById("app"));
utils.js
In version 5, all the utility functions that were previously accessed via import { utils } from '@rjsf/core';
are now available via import utils from '@rjsf/utils';
.
Because of the decoupling of validation from @rjsf/core
there is a breaking change for all the validator-based utility functions, since they now require an additional ValidatorType
parameter.
More over, one previously exported function resolveSchema()
is no longer exposed in the @rjsf/utils
, so use retrieveSchema()
instead.
Finally, the function getMatchingOption()
has been deprecated in favor of getFirstMatchingOption()
.
If you have built custom fields or widgets that utilized any of these breaking-change functions, don't worry, there is a quick and easy solution for you.
The registry
has a breaking-change which removes the previously deprecated definitions
property while adding the new schemaUtils
property.
This new registry.schemaUtils
property implements the SchemaUtilsType
interface, which allows you to call a version of each of these breaking-change functions without the need for passing either a validator
or rootSchema
.
Because all fields and widgets are guaranteed to be passed the registry
as a prop, if your custom field/widget happens to use either the registry.definitions
object or a breaking-change validator-based utility function you make the following changes:
import { RJSFSchema, FieldProps } from '@rjsf/utils';
function YourField(props: FieldProps) {
const { registry } = props;
// Change `registry.definitions` to `registry.rootSchema.definitions`
// const { definitions } = registry; <- version 4
const { rootSchema } = registry;
const { definitions } = rootSchema;
...
}
// Change breaking-change function to schemaUtils instead, otherwise import from @rjsf/utils
// import { utils } from '@rjsf/core'; <- version 4
// const { isMultiSelect, resolveSchema, getUiOptions } = utils; <- version 4
import { RJSFSchema, WidgetProps, getUiOptions } from '@rjsf/utils';
function YourWidget(props: WidgetProps) {
const { registry, uiSchema } = props;
const { schemaUtils } = registry;
// const matchingOption = getMatchingOption({}, options, rootSchema); <- version 4
// const isMultiSelect = isMultiSelect(schema, rootSchema); <- version 4
// const newSchema = resolveSchema(schema, formData, rootSchema); <- version 4
const matchingOption = schemaUtils.getFirstMatchingOption({}, options);
const isMultiSelect = schemaUtils.isMultiSelect(schema);
const newSchema: RJSFSchema = schemaUtils.retrieveSchema(schema, formData);
const options = getUiOptions(uiSchema);
...
}
validator.js
Because of the decoupling of validation from @rjsf/core
this file was refactored into its own @rjsf/validator-ajv8
package.
During that refactor a few breaking changes were made to how it works related to custom validation and ErrorSchema
conversion.
toErrorList param changed
In previous versions, the toErrorList()
function used to take a fieldName
string defaulted to root
, and used it to format the stack
message.
In version 5, fieldName
was changed to fieldPath
string array defaulted to an empty array, and is used to recursively add the field name to the errors as the property
object along with the raw message
.
The result is that if you had an ErrorSchema
that looks like:
const errorSchema: ErrorSchema = {
__errors: ['error message 1'],
password: { __errors: 'passwords do not match' },
};
The returned result from calling toErrorList(errorSchema)
has changed as follows:
// version 4 result
[{ stack: 'root: error message 1' }, { stack: 'password: passwords do not match' }][
// version 5 result
({ property: '.', message: 'error message 1', stack: '. error message 1' },
{
property: '.password',
message: 'passwords do not match',
stack: '.password passwords do not match',
})
];
Custom validation and extraErrors
In previous versions, when using a custom validator on the Form
, any errors that were generated were inconsistently inserted into the validations errors
list.
In addition, there was an issue where the non-stack
AJV error information was lost when custom validation generated errors.
This issue has been fixed.
Also, when extraErrors
were provided, they were being inconsistently inserted into the errors
list and the non-stack
AJV error information was lost.
In version 5, all of these errors will be consistently appended onto the end of the validation errors
list, and the additional AJV error information is maintained.
In other words, if custom validation or extraErrors
produced the following ErrorSchema
:
{
__errors: [ "Please correct your password"],
password2: { __errors: "passwords do not match" }
}
And the AJV validation produced the following ErrorSchema
:
{
__errors: [
{
message: 'should NOT be shorter than 3 characters',
name: 'minLength',
params: { limit: 3 },
property: '.password2',
schemaPath: '#/properties/password2/minLength',
stack: '.password2 should NOT be shorter than 3 characters',
},
];
}
The resulting errors
list has changed as follows:
// version 4
[
{ stack: 'root: Please correct your password' },
{ stack: 'password2: passwords do not match' },
{ stack: 'password2: should NOT be shorter than 3 characters' },
][
// version 5
({
message: 'should NOT be shorter than 3 characters',
name: 'minLength',
params: { limit: 3 },
property: '.password2',
schemaPath: '#/properties/password2/minLength',
stack: '.password2 should NOT be shorter than 3 characters',
},
{
property: '.',
message: 'Please correct your password',
stack: '. Please correct your password',
},
{
property: '.',
message: 'passwords do not match',
stack: '.password2 passwords do not match',
})
];
Generate correct ids when arrays are combined with anyOf
/oneOf
In v4, with arrays inside anyOf
or oneOf
, the parent name was not used to generate ids.
For example, given a schema such as:
{
"type": "object",
"properties": {
"items": {
"type": "array",
"items": {
"type": "object",
"anyOf": [
{
"properties": {
"foo": {
"type": "string"
}
}
},
{
"properties": {
"bar": {
"type": "string"
}
}
}
]
}
}
}
}
We would get fields with id root_foo
and root_bar
.
As you can imagine, we could end up with duplicated ids if there's actually a foo
or a bar
in the root of the schema.
From v5, the child fields will correctly use the parent id when generating its own id, generating ids such as root_items_0_foo
.
Deprecations added in v5
getMatchingOption()
The utility function getMatchingOption()
was deprecated in favor of the more aptly named getFirstMatchingOption()
which has the exact same implementation.
Non-standard enumNames
property
enumNames
is a non-standard JSON Schema field that was deprecated in version 5.
enumNames
could be included in the schema to apply labels that differed from an enumeration value.
This behavior can still be accomplished with oneOf
or anyOf
containing const
values, so enumNames
support will be removed from a future major version of RJSF.
For more information, see #532.
uiSchema.classNames
In versions previous to 5, uiSchema.classNames
was the only property that did not require the ui:
prefix.
Additionally, it did not support being added into the ui:options
object.
This was fixed in version 5 to be consistent with all the other properties in the uiSchema
, so the uiSchema.classNames
support may be removed from a future major version of RJSF.
If you are using classNames
as follows, simply add the ui:
prefix to it to remove the deprecation warning that will be displayed for each uiSchema.classNames
you have:
import { UiSchema } from '@rjsf/utils';
// This uiSchema will log a deprecation warning to the console
const uiSchemaLog: UiSchema = {
title: {
classNames: 'myClass',
},
};
// This uiSchema will not
const uiSchemaNoLog: UiSchema = {
title: {
'ui:classNames': 'myClass',
},
};
@rjsf/material-ui
BREAKING CHANGES
This theme was simplified back to working only with Material UI version 4 after an unsuccessful attempt to have it fully support both versions 4 and 5 simultaneously.
As a result, the MuiComponentContext
, MuiForm5
, Theme5
components and the useMuiComponent
hook were removed from the export.
In addition, the /v4
and /v5
sub-packages were also removed.
Migrating for Material UI version 4
If you were using this theme for Material UI version 4 AND you were using the sub-package, simply remove the /v4
from your imports.
If you modified your Typescript configuration for the /v4
sub-package, remove the following from your tsconfig.json
:
{
...
"compilerOptions": {
...
"baseUrl": ".",
"paths": {
"@rjsf/material-ui/*": ["node_modules/@rjsf/material-ui/dist/*"]
}
}
}
If you modified your Jest configuration for the /v4
sub-package, remove the following from your jest.config.json
:
"moduleNameMapper": {
"@rjsf/material-ui/v4": "<rootDir>/node_modules/@rjsf/material-ui/dist/v4.js"
},
Migrating for Material UI version 5
If you were using this theme for Material UI version 5, you will want to use @rjsf/mui
instead.
See below for some before and after examples.
If you modified your Typescript configuration for the /v5
sub-package, remove the following from your tsconfig.json
:
{
...
"compilerOptions": {
...
"baseUrl": ".",
"paths": {
"@rjsf/material-ui/*": ["node_modules/@rjsf/material-ui/dist/*"]
}
}
}
If you modified your Jest configuration for the /v5
sub-package, remove the following from your jest.config.json
:
"moduleNameMapper": {
"@rjsf/material-ui/v5": "<rootDir>/node_modules/@rjsf/material-ui/dist/v5.js"
},
Before
import Form5 from '@rjsf/material-ui';
or
import Form from '@rjsf/material-ui/v5';
or
import { withTheme } from '@rjsf/core';
import { Theme } from '@rjsf/material-ui/v5';
// Make modifications to the theme with your own fields and widgets
const Form = withTheme(Theme);
or
import { withTheme } from '@rjsf/core';
import { Theme as Theme5 } from '@rjsf/material-ui';
// Make modifications to the theme with your own fields and widgets
const Form = withTheme(Theme5);
After
import Form from '@rjsf/mui';
or
import { withTheme } from '@rjsf/core';
import { Theme } from '@rjsf/mui';
// Make modifications to the theme with your own fields and widgets
const Form = withTheme(Theme);