react/only-export-components flags a non-exported, nested, PascalCase function that returns JSX as if it were an exported component. Because the rule exists to protect React Fast Refresh — which is purely a property of a module's export surface — a function that is never exported cannot affect Fast Refresh, so it should not trigger the rule. The upstream reference implementation (eslint-plugin-react-refresh) does not flag it.
oxlint version
1.75.0
Config
Minimal reproduction
// repro.tsx
import React from "react";
// The file's only export is a NON-component.
export const helper = 1;
// This PascalCase, JSX-returning function is defined inside a factory and is
// NEVER exported.
function factory() {
return function Inner() {
return <span />;
};
}
void factory;
Actual behavior
repro.tsx:5:19: warning react(only-export-components): Fast refresh only works
when a file only exports components. Move your component(s) to a separate file.
The warning points at the non-exported inner function Inner.
Expected behavior
No warning. Inner is never exported, so it is invisible to Fast Refresh. The module's only export (helper) is a non-component, and a module that exports only non-components does not impede Fast Refresh.
Cross-check against the upstream rule
eslint-plugin-react-refresh (the rule oxlint mirrors) reports nothing on the exact same file.
What isolates the trigger
Two one-line changes each make the oxlint warning disappear, showing the trigger is specifically "a non-exported module-scope PascalCase function that returns JSX":
Note
This looks adjacent to #22039 ("align rule with upstream cases", merged) and #21702, but the non-exported nested-component case still reproduces on 1.75.0. The real-world pattern that surfaced it is a chart module that exports a single non-component render helper and internally uses a factory returning a named Bar-shape component for Recharts.
react/only-export-componentsflags a non-exported, nested, PascalCase function that returns JSX as if it were an exported component. Because the rule exists to protect React Fast Refresh — which is purely a property of a module's export surface — a function that is never exported cannot affect Fast Refresh, so it should not trigger the rule. The upstream reference implementation (eslint-plugin-react-refresh) does not flag it.oxlint version
1.75.0Config
{ "plugins": ["typescript", "react", "oxc"], "rules": { "react/only-export-components": ["warn", { "allowConstantExport": true }], }, }Minimal reproduction
Actual behavior
The warning points at the non-exported inner function
Inner.Expected behavior
No warning.
Inneris never exported, so it is invisible to Fast Refresh. The module's only export (helper) is a non-component, and a module that exports only non-components does not impede Fast Refresh.Cross-check against the upstream rule
eslint-plugin-react-refresh(the rule oxlint mirrors) reports nothing on the exact same file.What isolates the trigger
Two one-line changes each make the oxlint warning disappear, showing the trigger is specifically "a non-exported module-scope PascalCase function that returns JSX":
Rename the inner function to lowercase (
inner) → no warning.Export a real component from the file (so the export surface is all-components) → no warning:
Note
This looks adjacent to #22039 ("align rule with upstream cases", merged) and #21702, but the non-exported nested-component case still reproduces on 1.75.0. The real-world pattern that surfaced it is a chart module that exports a single non-component render helper and internally uses a factory returning a named
Bar-shape component for Recharts.