Skip to content

fix: add React 19 compatibility for getWidget (defaultOptions + remov…#4912

Open
Ashutoshx7 wants to merge 4 commits intorjsf-team:mainfrom
Ashutoshx7:fix/react-19-getwidget-4907
Open

fix: add React 19 compatibility for getWidget (defaultOptions + remov…#4912
Ashutoshx7 wants to merge 4 commits intorjsf-team:mainfrom
Ashutoshx7:fix/react-19-getwidget-4907

Conversation

@Ashutoshx7
Copy link

Reasons for making this change

This PR adds React 19 compatibility to getWidget.tsx by addressing two deprecated patterns:

  1. defaultProps.options: React 19 deprecated defaultProps for function components. This PR adds support for a new static defaultOptions property while maintaining backwards compatibility with the legacy pattern.

  2. react-is.isForwardRef(): React 19 removed this function. This PR replaces it with a more general component detection check using $$typeof.

fixes #4907

Changes Made

  • packages/utils/src/getWidget.tsx:

    • Added support for new defaultOptions static property (React 19 compatible)
    • Maintained backwards compatibility with defaultProps.options (deprecated but still works)
    • Replaced ReactIs.isForwardRef() with general $$typeof check for exotic components
  • packages/utils/test/getWidget.test.tsx: Updated tests to use the new defaultOptions pattern

  • CHANGELOG.md: Added entry under @rjsf/utils

  • packages/docs/docs/advanced-customization/custom-widgets-fields.md: Updated documentation to show new pattern

  • packages/docs/docs/migration-guides/v7.x upgrade guide.md: Created migration guide for the deprecation

Checklist

  • I'm updating documentation
    • I've checked the rendering of the Markdown text I've added
  • I'm adding or updating code
    • I've added and/or updated tests. I've run npx nx run-many --target=build --exclude=@rjsf/docs && npm run test:update to update snapshots, if needed.
    • I've updated docs if needed
    • I've updated the changelog with a description of the PR
  • I'm adding a new feature
    • I've updated the playground with an example use of the feature

…e isForwardRef)

fixes rjsf-team#4907

- Add support for new static defaultOptions property on widgets (React 19 compatible)

- Maintain backwards compatibility with legacy defaultProps.options pattern

- Replace deprecated react-is.isForwardRef() with general component detection

- Update documentation and add migration guide
@Ashutoshx7 Ashutoshx7 force-pushed the fix/react-19-getwidget-4907 branch from bfbb128 to 1dea32a Compare January 5, 2026 17:53
* @param AWidget - A widget that will be wrapped or one that is already wrapped
* @returns - The wrapper widget
*/
function mergeWidgetOptions<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(
Copy link
Contributor

@nickgros nickgros Jan 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know that we want to maintain this defaultOptions pattern. Custom widget authors could implement this in their widgets if they want to. With defaultProps gone in React 19, I think we may want to just remove mergeWidgetOptions in RJSF v7. With that in mind, I don't think we should add this defaultOptions compatibility feature. @Ashutoshx7, is there any reason that I'm not realizing that explains why we need to support this in RJSF?

Copy link
Member

@heath-freenome heath-freenome Jan 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nickgros You raise a good point and I believe you are correct. I am curious to hear what @Ashutoshx7 has to say in reply to your question.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry for the late reply !

The main motivation behind this change was to provide a smooth React 19 transition for existing custom widgets that currently rely on defaultProps.options, without breaking them immediately. The idea behind defaultOptions was to offer a React-19-safe replacement while keeping backward compatibility during the v7 lifecycle.

I’ll update the PR to drop the defaultOptions support and limit it to addressing the React 19 deprecations only (removing defaultProps usage and replacing react-is.isForwardRef).

I’ll push the updated changes shortly.

@nickgros
@heath-freenome

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you actually change anything in this file or is it just whitespace. If just whitespace, please revert

Comment on lines +103 to 107
// Check if widget is a valid React component.
// We exclude strings because in RJSF, string widgets are keys in the registry, not HTML elements.
if (ReactIs.isValidElementType(widget) && typeof widget !== 'string') {
return mergeWidgetOptions<T, S, F>(widget as Widget<T, S, F>);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, once we drop defaultProps support in v7 this whole conditional and the mergeWidgetOptions() function will no longer be necessary. Which means that the MergedWidget feature will just go away.

TestRefWidget.defaultProps = { options: { id: 'test-id' } };

function TestWidget(props: WidgetProps) {
const { options } = props;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Convert defaultProps into a destructure default assignment

Suggested change
const { options } = props;
const { options, id = 'foo' } = props;

Comment on lines 52 to 55
function TestWidgetDefaults(props: WidgetProps) {
const { options } = props;
return <div {...options}>test</div>;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The replacement fordefaultProps.options merging

Suggested change
function TestWidgetDefaults(props: WidgetProps) {
const { options } = props;
return <div {...options}>test</div>;
}
function TestWidgetDefaults(props: WidgetProps) {
const { options } = props;
const allOptions = { color: 'yellow', ...options };
return <div {...allOptions}>test</div>;
}

Comment on lines +57 to 60
TestWidgetDefaults.defaultProps = { options: { color: 'yellow' } };


const widgetProps: WidgetProps = {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
TestWidgetDefaults.defaultProps = { options: { color: 'yellow' } };
const widgetProps: WidgetProps = {
const widgetProps: WidgetProps = {

Comment on lines +45 to 47
TestRefWidget.defaultProps = { options: { id: 'test-id' } };

function TestWidget(props: WidgetProps) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We would then also drop testing with .defaultProps and instead roll that into the widget implementation instead in a manner similar to the changes to line 54 below

const { options } = props;
const allOptions = { id: 'test-id', ...options };
Suggested change
TestRefWidget.defaultProps = { options: { id: 'test-id' } };
function TestWidget(props: WidgetProps) {
function TestWidget(props: WidgetProps) {

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Fix getWidgets to stop using deprecated defaultProps for options and react-is.isForwardRef()

3 participants