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 (rjsf-team#2197)
Version 5 is dropping official support for Node 12 as it is no longer a maintained version.
Please use Node 16 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.
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.
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:
createSchemaUtils()
,getInputProps()
,mergeValidationData()
andprocessSelectValue()
- 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.
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.
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.
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"));
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();
Additionally, in version 5, the validate
prop on Form
was renamed to customValidate
to avoid confusion with the new validator
prop.
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"));
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.
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"));
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.
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 isMultiSelect = isMultiSelect(schema, rootSchema); <- version 4
// const newSchema = resolveSchema(schema, formData, rootSchema); <- version 4
const isMultiSelect = schemaUtils.isMultiSelect(schema);
const newSchema: RJSFSchema = schemaUtils.retrieveSchema(schema, formData);
const options = getUiOptions(uiSchema);
...
}
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.
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 = {
__error: [ "error message 1"],
password: { __error: "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" }
]
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
:
{
__error: [ "Please correct your password"],
password2: { __error: "passwords do not match" }
}
And the AJV validation produced the following ErrorSchema
:
{
__error: [{
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" }
]
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
.
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 may be removed from a future major version of RJSF.
For more information, see #532.
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:
// This uiSchema will log a deprecation warning to the console
const uiSchema = {
title: {
"classNames": "myClass"
}
};
// This uiSchema will not
const uiSchema = {
title: {
"ui:classNames": "myClass"
}
};
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.
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"
},
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"
},
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 Theme5 from '@rjsf/material-ui';
// Make modifications to the theme with your own fields and widgets
const Form = withTheme(Theme5);
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);