Skip to main content
interlace
Plugin: react-featuresRules

require-optimization

require-optimization rule

Suggest performance optimizations for React components based on usage patterns. This rule is part of eslint-plugin-react-features.

Quick Summary

AspectDetails
SeveritySuggestion (performance)
Auto-Fix❌ No (requires manual optimization)
CategoryReact
ESLint MCP✅ Optimized for ESLint MCP integration
Best ForReact applications needing performance tuning

Rule Details

Analyzes React components and suggests appropriate optimizations based on their structure, props count, and usage patterns.

Why This Matters

IssueImpactSolution
🔄 Unnecessary re-rendersPoor performanceUse React.memo
🐛 Expensive computationsSlow rendersUse useMemo
🔍 Function recreationChild component re-rendersUse useCallback

Optimization Messages

Message IDWhen SuggestedOptimization
considerMemoComponent has many propsWrap with React.memo()
considerUseMemoContains array operationsWrap computation with useMemo()
considerUseCallbackInline event handlersWrap with useCallback()
considerLazyLarge component (50+ lines)Use React.lazy()
considerPureComponentClass component with many propsExtend PureComponent

Examples

❌ Unoptimized

// Component with many props - may benefit from memo
function UserCard({ name, email, avatar, role, status, onClick, onEdit }) {
  // Expensive computation on every render
  const permissions = roles.map(r => getPermissions(r)).flat();
  
  // Inline function recreated every render
  return (
    <Card onClick={() => onClick(name)}>
      <Avatar src={avatar} />
      <div>{name}</div>
      <div>{email}</div>
    </Card>
  );
}

// Class component that could be PureComponent
class DataTable extends React.Component {
  render() {
    const { data, columns, sortBy, filterBy, onSort, onFilter } = this.props;
    return <Table data={data} columns={columns} />;
  }
}

✅ Optimized

// GOOD: Memoized component with many props
const UserCard = React.memo(function UserCard({ 
  name, email, avatar, role, status, onClick, onEdit 
}) {
  // GOOD: Expensive computation memoized
  const permissions = useMemo(
    () => roles.map(r => getPermissions(r)).flat(),
    [roles]
  );
  
  // GOOD: Callback memoized
  const handleClick = useCallback(() => {
    onClick(name);
  }, [onClick, name]);
  
  return (
    <Card onClick={handleClick}>
      <Avatar src={avatar} />
      <div>{name}</div>
      <div>{email}</div>
    </Card>
  );
});

// GOOD: Using PureComponent for shallow comparison
class DataTable extends React.PureComponent {
  render() {
    const { data, columns, sortBy, filterBy, onSort, onFilter } = this.props;
    return <Table data={data} columns={columns} />;
  }
}

// GOOD: Large component with code splitting
const Dashboard = React.lazy(() => import('./Dashboard'));

function App() {
  return (
    <Suspense fallback={<Loading />}>
      <Dashboard />
    </Suspense>
  );
}

Options

OptionTypeDefaultDescription
suggestMemobooleantrueSuggest React.memo for components
suggestUseMemobooleantrueSuggest useMemo for expensive computations
suggestUseCallbackbooleantrueSuggest useCallback for event handlers
minPropsForMemonumber3Minimum props count to suggest memo

Configuration with Options

{
  rules: {
    'react-features/require-optimization': ['warn', {
      suggestMemo: true,
      suggestUseMemo: true,
      suggestUseCallback: true,
      minPropsForMemo: 5  // More conservative threshold
    }]
  }
}

Configuration Examples

Basic Usage

{
  rules: {
    'react-features/require-optimization': 'warn'
  }
}

Conservative (fewer suggestions)

{
  rules: {
    'react-features/require-optimization': ['warn', {
      minPropsForMemo: 6,
      suggestUseCallback: false  // Only for critical paths
    }]
  }
}

Further Reading

Known False Negatives

considerMemo is decided by structure: a PascalCase function component with at least minPropsForMemo destructured props that is never passed to React's memo. The other suggestions are heuristics, and they report on a guess:

  • considerUseMemo reads any .map / .filter / .reduce / .find call as expensive;
  • considerUseCallback reports every inline handler, whether or not a memoized child receives it;
  • considerLazy counts lines;
  • considerPureComponent counts a class's destructured props.

(props) => … counts as one prop, since the props an object parameter carries are not listed in the code.

Use this rule

npm install -D oxlint-plugin-react-features
.oxlintrc.json
{  "jsPlugins": ["oxlint-plugin-react-features"],  "rules": {    "react-features/require-optimization": "error"  }}
vite.config.ts
import { defineConfig } from 'vite-plus';export default defineConfig({  lint: {    jsPlugins: ['oxlint-plugin-react-features'],    rules: {      'react-features/require-optimization': 'error',    },  },});

oxlint-plugin-react-features re-exports eslint-plugin-react-features/oxlint: the same rules under the same IDs, and no ESLint install. Why oxlint.

Did this rule catch something? Star the repo to get new CWE coverage as we ship it — or follow the AI-code-security benchmarks behind these rules.