fix(ui-library): replace defaultProps with ES6 defaults for React 19#23317
Merged
enricobattocchi merged 3 commits intoMay 29, 2026
Merged
Conversation
Coverage Report for CI Build 0Warning Build has drifted: This PR's base is out of sync with its target branch, so coverage data may include unrelated changes. Coverage increased (+0.01%) to 57.791%Details
Uncovered ChangesNo uncovered changes found. Coverage Regressions13 previously-covered lines in 3 files lost coverage.
Coverage Stats💛 - Coveralls |
Contributor
There was a problem hiding this comment.
Pull request overview
This PR prepares @yoast/ui-library components for React 19 by replacing function/forwardRef component defaultProps with ES6 default parameters.
Changes:
- Moves component defaults into parameter destructuring across UI library elements and components.
- Removes
defaultPropsassignments from the converted components. - Adds complexity lint disables where the default-parameter conversion increases reported complexity.
Reviewed changes
Copilot reviewed 33 out of 33 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
packages/ui-library/src/elements/validation/validation-input.js |
Removes defaultProps after moving validation defaults. |
packages/ui-library/src/elements/tooltip/index.js |
Moves tooltip defaults into destructuring. |
packages/ui-library/src/elements/toggle/index.js |
Converts toggle defaults and updates lint directive. |
packages/ui-library/src/elements/title/index.js |
Converts title defaults and removes no-undefined directive. |
packages/ui-library/src/elements/textarea/index.js |
Converts textarea defaults. |
packages/ui-library/src/elements/text-input/index.js |
Converts text input defaults. |
packages/ui-library/src/elements/tag-input/index.js |
Converts tag input defaults and adds complexity disable. |
packages/ui-library/src/elements/table/index.js |
Removes table defaultProps. |
packages/ui-library/src/elements/spinner/index.js |
Converts spinner defaults. |
packages/ui-library/src/elements/select/index.js |
Converts select defaults and adds complexity disable. |
packages/ui-library/src/elements/radio/index.js |
Converts radio defaults and adds complexity disable. |
packages/ui-library/src/elements/progress-bar/index.js |
Removes progress bar defaultProps. |
packages/ui-library/src/elements/paper/index.js |
Removes paper defaultProps. |
packages/ui-library/src/elements/link/index.js |
Converts link defaults. |
packages/ui-library/src/elements/label/index.js |
Converts label defaults. |
packages/ui-library/src/elements/file-input/index.js |
Converts file input defaults. |
packages/ui-library/src/elements/code/index.js |
Removes code defaultProps. |
packages/ui-library/src/elements/checkbox/index.js |
Removes checkbox defaultProps. |
packages/ui-library/src/elements/button/index.js |
Converts button defaults and adds complexity disable. |
packages/ui-library/src/elements/badge/index.js |
Converts badge defaults. |
packages/ui-library/src/elements/autocomplete/index.js |
Converts autocomplete defaults and adds complexity disable. |
packages/ui-library/src/elements/alert/index.js |
Removes alert defaultProps. |
packages/ui-library/src/components/toggle-field/index.js |
Converts toggle field defaults and adds complexity disable. |
packages/ui-library/src/components/textarea-field/index.js |
Converts textarea field defaults and adds complexity disable. |
packages/ui-library/src/components/text-field/index.js |
Converts text field defaults and adds complexity disable. |
packages/ui-library/src/components/tag-field/index.js |
Converts tag field defaults and adds complexity disable. |
packages/ui-library/src/components/stepper/index.js |
Removes stepper defaultProps. |
packages/ui-library/src/components/select-field/index.js |
Converts select field defaults and adds complexity disable. |
packages/ui-library/src/components/modal/index.js |
Converts modal subcomponent defaults. |
packages/ui-library/src/components/modal/container.js |
Converts modal container defaults. |
packages/ui-library/src/components/file-import/index.js |
Converts file import defaults. |
packages/ui-library/src/components/card/index.js |
Converts card defaults. |
packages/ui-library/src/components/autocomplete-field/index.js |
Converts autocomplete field defaults and adds complexity disable. |
Comments suppressed due to low confidence (1)
packages/ui-library/src/elements/tooltip/index.js:39
- This conversion removes
variantfrom the rest props, so it is no longer forwarded to the underlyingComponent. Before this change,variantstayed insidepropsand was spread ontoComponent, including for customascomponents; to keep this behavior-neutral, pass it through explicitly when spreading the remaining props.
1d9c084 to
4adbb50
Compare
…eact 19 React 19 ignores `defaultProps` on function/forwardRef components, so any component that relied on them received `undefined` for those props and threw when the value was dereferenced (e.g. Title's `as`, Button's `variant`), crashing the whole React tree. This is what stopped the Yoast metabox from rendering on the Gutenberg 23.3 RC (React 19.2.4). Move every defaulted prop into ES6 default parameters across the ui-library components that used `defaultProps` (33 files) and remove the now-unused `.defaultProps` blocks. Behaviour is unchanged on React 18. The 12 components the conversion pushed over ESLint's `complexity` warning threshold (each ES6 default counts as a branch) get a file-level `eslint-disable complexity`, matching the existing convention in this repo (e.g. ui-library's components/notifications and ~118 other files). Refs: Yoast/reserved-tasks#231, Yoast/reserved-tasks#246 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…faults
`defaultProps` created one stable instance per object/array default, but the
ES6 conversion replaced them with inline `{}` / `[]` literals that allocate a
fresh value every render. Where such a default feeds a hook dependency array
this broke referential equality and re-fired effects in a loop:
- Stepper: `steps` is in a useLayoutEffect dependency list that calls
setStepRefs, so rendering <Stepper> with children but no `steps` looped.
- Select: `options` feeds a useMemo dependency, recomputed every render.
- TagInput: `tags` feeds a useCallback dependency, reallocated every render.
Hoist every empty object/array default to a module-level constant (matching the
old defaultProps single instance). The remaining ones (validation/labelProps/
buttonProps) are not in dependency arrays or passed to memoized children today,
but are stabilized too for parity and future safety.
Refs: Yoast/reserved-tasks#231, Yoast/reserved-tasks#246
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…up, RadioGroup, Root
Not part of the defaultProps->ES6 conversion: these components already used
inline `[]` / `{}` defaults in trunk (never defaultProps-backed), so each
allocates a fresh value every render. CheckboxGroup's `values` feeds a
useCallback dependency, so a fresh `[]` reallocated the change handler on every
render; RadioGroup's `options` and Root's `context` are stabilized for parity
(not currently in dependency arrays). Hoist all three to module-level constants.
Found while auditing the React 19 defaultProps conversion.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
dff3f27 to
c489731
Compare
df47d0a
into
feature/fix-gutenberg-23.3
26 of 42 checks passed
This was referenced May 29, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Context
React 19 ignores
defaultPropson function/forwardRefcomponents, so props that relied on them arriveundefinedand throw when dereferenced (e.g.Title'sas,Button'svariant), which crashed the whole Yoast metabox on the Gutenberg 23.3 RC (React 19.2.4). This moves every@yoast/ui-librarycomponent that useddefaultPropsto ES6 default parameters. No net behaviour change on React 18.3; it is React 19 prep for WordPress 7.1.Verified on a React 19.2.4 site (Gutenberg 23.3-RC1): with this change the Yoast metabox, sidebar and Settings render with a clean console; without it the metabox fails to render. The follow-up render smoke test (separate PR) also passes 58/58
@yoast/ui-librarycomponents on both React 18.3 and 19.2.4. Sibling of #23316.Summary
This PR can be summarized in the following changelog entry:
defaultPropswith ES6 default parameters across@yoast/ui-librarycomponents so they keep rendering on React 19, which ignoresdefaultPropsfor function components.Relevant technical choices:
The PR has three commits:
complexitywarning get a file-leveleslint-disable complexity(matching the existing ~118-file convention in this repo; the threshold itself is unchanged).Tooltip'svariantwas previously forwarded via...props; it is re-forwarded explicitly so the change stays behaviour-neutral.defaultPropsprovided one stable instance; inline{}/[]defaults allocate a fresh value each render, breaking dependency-array identity. Left as-is this regressed three components (Stepper'sstepslooped a layout effect, Select'soptionsfed auseMemo, TagInput'stagsfed auseCallback). Hoisted every such default to a module-level constant.CheckboxGroup,RadioGroup, andRoot, which already used inline[]/{}defaults in trunk (neverdefaultProps-backed);CheckboxGroup'svaluesfeeds auseCallbackdependency. Found while auditing the conversion, hence the separate commit.Test instructions
Test instructions for the acceptance test before the PR gets merged
This PR can be acceptance tested by following these steps:
@yoast/ui-libraryUI (Settings, metabox, buttons, fields, modals).Relevant test scenarios
The key check is the browser console in the block editor on a React 19 build: the Yoast metabox and sidebar must render.
Test instructions for QA when the code is in the RC
QA tests on the Gutenberg 23.3-RC1 (React 19) build, following the acceptance test steps above.
Impact check
This PR affects the following parts of the plugin, which may require extra testing:
@yoast/ui-librarycomponents: the Yoast SEO Settings UI, the metabox/sidebar, and add-ons that consume the package.Other environments
[shopify-seo], added test instructions for Shopify and attached theShopifylabel to this PR.[yoast-doc-extension], added test instructions for Yoast SEO for Google Docs and attached theGoogle Docs Add-onlabel to this PR.Documentation
Quality assurance
grunt build:imagesand committed the results, if my PR introduces or edits images or SVGs.Innovation
innovationlabel.Refs Yoast/reserved-tasks#231, Yoast/reserved-tasks#246
Fixes Yoast/reserved-tasks#1250
🤖 Generated with Claude Code