Dev-only React 19 warning: `ref` reaches React.Fragment in Search result rendering
Summary
Under React 19, rendering search results with the built-in <Search> component logs a
React development warning:
Invalid prop `ref` supplied to `React.Fragment`. React.Fragment can only have `key` and `children` props.The warning comes from React's development-only Fragment validator. Search functions correctly and production builds are unaffected.
Environment
| Item | Version |
|---|---|
| nextra / nextra-theme-docs | 4.6.1 |
| next | 15.5.20 (Webpack) |
| react / react-dom | 19.2.7 |
| @headlessui/react | 2.2.10 (transitive) |
| Node | 24 |
| OS | macOS |
Reproduction steps
- Create a Nextra 4 docs site using the default
nextra-theme-docs<Layout>, passing nosearchprop. - Run
next buildso a Pagefind index is generated, then serve the output — or runnext devwith a Pagefind index present inpublic/. - Open the site, focus the search input, and type a query that returns results.
- Observe the browser console.
Expected behavior
No React warnings when rendering search results.
Actual behavior
Multiple warnings are logged — 7 to 10 occurrences observed across runs, for a query returning 47 results.
Warnings appear only after a query is typed. None occur at page load or when focusing the search input.
Root cause
React 19 made ref a regular prop. validateFragmentProps in react-dom enumerates
element.props and rejects any key other than children and key, so a ref reaching a
Fragment now warns. Under React 18 ref lived on element.ref and never appeared in
props, which is why this did not surface previously.
validateFragmentProps exists only in React's development build.
Verified evidence
We patched validateFragmentProps in the vendored react-dom development build to dump
element._debugStack, identifying the JSX creation site:
FRAGMENT_REF_TRACE owner=unknown props=children,ref
at exports.jsxs (react/cjs/react-jsx-runtime.development.js:335:13)
at Search (nextra/dist/client/components/search.js:361:97)
at renderWithHooks → updateFunctionComponent → beginWorkThe element carries props = children,ref at runtime.
The corresponding source is the root return of the module-private Result component in
nextra/dist/client/components/search.js:
const Result = (t0) => {
const { data } = t0;
...
t4 = jsxs(Fragment, { children: [ t2, t3 ] }); // line 361
return t4;
}The declared props are children only, so ref is injected downstream. We did not isolate
the injecting mechanism and make no claim about it.
We also inspected [email protected]. Result remains module-private (line 312) and
still returns the identical jsxs(Fragment, { children: [t2, t3] }) as its root return
(line 342). That build likewise exports only Search and importPagefind.
Why downstream consumers cannot fix this
Resultis not exported —search.jsexports onlySearchandimportPagefind.- The Fragment is
Result's unconditional root return, not behind any branch or flag. - No documented
<Search>prop reaches it. The public API isemptyResult,errorText,loading,placeholder,className,searchOptions,onSearch, which govern the input and the empty/error/loading states only. NextraConfig.searchcontrols index generation — it setsdata-pagefind-bodyon<main>anddata-pagefind-ignoreon code blocks — not result rendering.PagefindSearchOptionsgoverns the Pagefind query only.- No published version resolves it: 4.6.1 is the latest stable release, and 5.0.0-alpha.24 retains the same implementation.
The reproducing application contains no ref, Fragment, or forwardRef usage in any of
its source or content files (verified by grep). It renders <Layout> with no search prop,
so the default <Search> is used. The React 19 stack trace points at the application's
RootLayout because that is the React owner stack, not the source.
Production impact
None. A production build served from the exported output was measured at 0 Fragment warnings and 0 console errors, with search returning 47 results for the same query that produces warnings in development.
Repository used for reproduction
https://github.com/ihateresume/ihateresume — apps/docs
Downstream tracking issue: https://github.com/ihateresume/ihateresume/issues/296
Source: shuding/nextra