Repository navigation
Conversation
--- <!-- Thank you for sending the PR!
TheGeekyM
marked this pull request as ready for review
November 6, 2026 07:18
This branch has not been deployed
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.
Refs #17113
Summary
The Jest file transform turns each
.svgimport into aReactComponent, and that component builds its React element by hand. React 19 does not accept that hand-built object. This PR has the component callReact.createElement('svg', ...)instead. It also turns the kitchensink SVG component tests back on, since they had been skipped because of this bug.Motivation
packages/react-scripts/config/jest/fileTransform.jsis the Jest transform for every non-JS asset in a CRA project. For.svgfiles it creates aReactComponentexport that stands in for SVGR during tests. The component used to return a literal element object:This depends on React's internal element shape. React 19 changed that shape in two ways:
Symbol.for('react.transitional.element'). React 19 no longer accepts an object taggedreact.elementas an element, so rendering the component fails.refis now a regular prop (element.props.ref). The top-levelelement.reffield is deprecated.Any project that runs
react-scripts teston React 19 and renders a component imported withimport { ReactComponent } from './x.svg'hits this. The repo already shows the problem: the kitchensink fixtureSvgComponent.test.jswas markeddescribe.skipwith the comment// TODO: these fail with React 19 due to the JSX transform mismatch.Both its render test and its ref-forwarding test were disabled.What changed
packages/react-scripts/config/jest/fileTransform.js: TheforwardRefrender function in the generated SVG module now returnsReact.createElement('svg', Object.assign({}, props, { ref: ref }), filename). React builds the element itself, so it always has the right$$typeofand ref handling for whichever React version is installed. Non-SVG files are unchanged and still export only the basename.packages/react-scripts/fixtures/kitchensink/template/src/features/webpack/SvgComponent.test.js: Changeddescribe.skipback todescribeand removed the TODO comment. The existing render and ref tests now cover the fix end to end.packages/react-scripts/config/jest/__tests__/fileTransform.test.js(new): Unit tests for the transform's output that don't require building the kitchensink app.How it works
The generated module still
requiresreactfrom the project being tested. The only difference is that the element now comes fromReact.createElementinstead of an object literal:refgoes through the config object. React 17/18 move it intoelement.ref. React 19 keeps it inelement.props.ref. In both cases React DOM attaches it to the rendered<svg>node.props.childrenas before.Object.assign({}, props, ...)copies the props, so the caller's props object is never modified. The old code did the same.forwardRefnamedSvg<PascalCaseFilename>, which matches SVGR's naming. Snapshots and component names in test output stay the same.Alternatives I considered:
React.versionand choosingreact.elementorreact.transitional.element: This keeps the code tied to React internals. It would also need to copy other details of the element shape (_owner,_store, how ref is stored) that change between versions. It would break again the next time the shape changes.react/jsx-runtime'sjsx(): This works, butcreateElementis the stable public API that every supported React version has. It is enough for one element with static children..svgto a static mock file throughmoduleNameMapper: That would change the transform's public behavior (per-file component names, thedefaultexport) for every project. It's a much bigger change than this bug needs.This PR does not touch the other Jest transforms and does not add a React 17/18/19 CI matrix. Those were part of a larger proposal and are better handled separately.
Tests
New file:
packages/react-scripts/config/jest/__tests__/fileTransform.test.js. It runs the transform's output throughnew Functionand checks the module it exports:logo.png).module.exports = "data.txt";and does not reference React.defaultis the basename and__esModuleistrue.ReactComponentis aforwardRefwhose render function is namedSvgMyLogo, the same as SVGR.React.isValidElementfor the installed React version. This is the regression check for React 19.svgand its children are the filename.classNameandwidthare passed to the<svg>.getRefhelper readsprops.ref(React 19) orelement.ref(React 17/18), so the test works on all three.ReactDOMServer.renderToStaticMarkupoutputs<svg class="icon">my-logo.svg</svg>.Re-enabled:
packages/react-scripts/fixtures/kitchensink/template/src/features/webpack/SvgComponent.test.jsrenders without crashing: rendersSvgComponentwithcreateRootand checks that the text content islogo.svg.svg root element equals the passed ref: rendersSvgComponentWithRefand checks that the ref points to the single rendered<svg>DOM node.To run:
The kitchensink test also runs in the existing kitchensink e2e flow, which uses the transform through
react-scripts test.Compatibility & risk
React.createElementwith arefin the config has worked the same way since long before React 17. The rendered DOM, the component name, thedefaultexport and the non-SVG path are all unchanged. On React 17/18 the element now also has the internal fields thatcreateElementadds, so it is closer to a real element than the hand-built one was.ReactComponent.render(...), which is unusual.createElementcall per render, which is what SVGR's real output does anyway.react-scripts.ref: nullis handled when no ref is passed (React 17/18 setelement.ref = null, React 19 putsref: nullin props; both render the same), and whether the newconfig/jest/__tests__file is picked up by the repo's root Jest config in CI.