React Compiler Explained Do You Still Need useMemo, useCallback, and React.memo

Summarize this blog post with:

TL;DR: React Compiler can automatically optimize many memoization patterns that it can safely analyze, reducing the need for routine useMemo, useCallback, and React.memo usage. Learn what the compiler optimizes, where manual memoization still adds value, how performance best practices change, and how to enable React Compiler in Next.js and Vite projects.

You open a React component and see a familiar pattern:

  • A couple of useMemo calls
  • Several useCallback hooks
  • A React.memo wrapper around a child component

Everything looks optimized. Yet the component is still difficult to read, nobody wants to touch the dependency arrays, and it’s not even clear whether any of those optimizations are helping.

For years, this was normal React development.

If a value was an object, we wrapped it in useMemo. If a function was passed to a child component, we reached for useCallback. If a component re-rendered too often, we added React.memo and hoped for the best.

React Compiler changes that workflow.

Instead of manually sprinkling memoization throughout your application, React Compiler can automatically optimize many common memoization patterns that it can safely analyze during the build process. The result is simpler components, fewer dependency arrays, and less performance-related boilerplate.

But does that mean useMemo, useCallback, and React.memo are officially obsolete?

Not quite.

The real answer is more practical than the headlines suggest.

Build production-ready React applications without rebuilding your UI foundation. Access 145+ enterprise-grade components designed for performance, consistency, and scale.

Why manual memoization became a habit

Before React Compiler, developers often optimized for reference stability rather than real performance problems.

They commonly used:

  • useMemo to cache calculated values.
  • useCallback to keep function references stable.
  • React.memo to prevent child components from re-rendering when props were unchanged.

A typical component slowly turns into this:

import { useMemo, useCallback } from "react";

function ProductSearch({ products, query, onSelect }) {
  const visibleProducts = useMemo(() => {
    return products.filter((product) =>
      product.name.toLowerCase().includes(query.toLowerCase())
    );
  }, [products, query]);

  const handleSelect = useCallback(
    (id) => {
      onSelect(id);
    },
    [onSelect]
  );

  return (
    <ProductList
      products={visibleProducts}
      onSelect={handleSelect}
    />
  );
}

There’s nothing wrong with this approach.

The problem is that many teams adopted it by default, even when no measurable performance benefit existed.

Over time, components became harder to maintain because developers had to:

  • Keep dependency arrays accurate
  • Avoid stale closures
  • Understand memoization behavior across the component tree
  • Review optimization code that often delivered little value

The end result was frequently more complexity than performance.

What is React Compiler?

React Compiler is a build-time optimization tool that analyzes React components and automatically applies memoization when it can safely prove that doing so won’t change behavior.

In practical terms, it allows developers to write straightforward React code while letting the compiler handle many of the optimization decisions behind the scenes.

Consider this example:

function ProductSearch({ products, query, onSelect }) {
  const visibleProducts = products.filter((product) =>
    product.name.toLowerCase().includes(query.toLowerCase())
  );

  const handleSelect = (id) => {
    onSelect(id);
  };

  return (
    <ProductList
      products={visibleProducts}
      onSelect={handleSelect}
    />
  );
}

Without a compiler, many developers would automatically add useMemo and useCallback.

With React Compiler enabled, that extra code is often unnecessary.

The important detail is that React Compiler only optimizes patterns it can safely analyze. If a component violates React rules or contains patterns the compiler cannot guarantee, it simply skips those optimizations.

That means the goal isn’t to stop thinking about performance. The goal is to stop optimizing everything preemptively.

When React Compiler can make manual useMemo and useCallback unnecessary

React Compiler is particularly effective in situations where developers previously used memoization as a precaution rather than a necessity.

Derived values

A common example is calculating filtered or transformed data from props.

function InvoiceSummary({ invoices, status }) {
  const filteredInvoices = invoices.filter(
    (invoice) => invoice.status === status
  );

  const total = filteredInvoices.reduce(
    (sum, invoice) => sum + invoice.amount,
    0
  );
  return <Summary total={total} invoices={filteredInvoices} />;
}

Before React Compiler, many developers would wrap filteredInvoices and total in useMemo.
With React Compiler enabled, this kind of pure derived value may be a good candidate for automatic optimization. The compiler’s ability to optimize it depends on whether the calculation is pure and follows patterns the compiler supports. Not every derived value is guaranteed to be cached.

Inline event handlers

Another common pattern is wrapping every event handler with useCallback.

function TodoItem({ todo, onToggle }) {
  const handleChange = () => {
    onToggle(todo.id);
  };

  return (
    <label>
      <input
        type="checkbox"
        checked={todo.completed}
        onChange={handleChange}
      />
      {todo.title}
    </label>
  );
}

Many teams previously used useCallback here solely to preserve function identity.

In compiler-enabled applications, this level of manual optimization is often unnecessary.

Lightweight object props

Developers also frequently memoized simple configuration objects.

function ChartPanel({ data, theme }) {
  const chartOptions = {
    color: theme.primaryColor,
    showLegend: true,
  };

  return <RevenueChart data={data} options={chartOptions} />;
}

React Compiler can often optimize these scenarios without requiring additional hooks.

When you still need useMemo

React Compiler reduces routine memoization, but it does not eliminate legitimate performance bottlenecks.

Use useMemo when:

  • A calculation is genuinely expensive
  • Profiling shows measurable render costs
  • Inputs change far less frequently than renders

Caching improves user-visible performance

function AnalyticsView({ events }) {
  const report = useMemo(() => {
    return buildLargeReport(events);
  }, [events]);

  return <ReportTable report={report} />;
}

Even if buildLargeReport() processes thousands of records and performs grouping, sorting, and aggregation, an expensive calculation does not automatically require manual memoization. React Compiler may already optimize the calculation when it can safely analyze the code. If profiling shows that the calculation is still a bottleneck, keeping useMemo may be appropriate.

The key difference in 2026 is intent.

  • You’re no longer adding useMemo because a value is an array or object.
  • You’re adding it because you’ve identified an actual problem.

When you still need useCallback

The strongest use case for useCallback is when function identity becomes part of an integration contract.

function MapView({ map, selectedId }) {
  const handleMarkerClick = useCallback(
    (markerId) => {
      map.focusMarker(markerId);
    },
    [map]
  );

  useMapMarkerEvents(map, handleMarkerClick);

  return <MapCanvas selectedId={selectedId} />;
}

In this scenario, the callback is tied to an external system.

Removing useCallback may cause unnecessary subscriptions, listener re-registration, or unexpected behavior.

Keep useCallback when:

  • Third-party libraries compare callback references
  • Custom hooks depend on stable callbacks
  • Removing it creates measurable regressions
  • Reference identity is part of the API contract

When you still need React.memo

React.memo remains useful for expensive components that receive stable props.

const LargeRow = React.memo(function LargeRow({ row, onOpen }) {
  return (
    <div className="row" onClick={() => onOpen(row.id)}>
      {row.title}
    </div>
  );
});

The mistake many teams make is wrapping everything with React.memo.

A component should earn memoization through profiling, not receive it by default.

Use it when:

  • Rendering is expensive
  • Props remain stable
  • Profiling confirms benefits

Skip it when:

  • Props change constantly
  • Rendering is cheap
  • Comparison costs exceed render costs

React Compiler vs Traditional Memoization

The easiest way to think about React Compiler is this: React Compiler is your baseline optimization strategy.

Manual memoization becomes the exception, not the default.

A practical workflow now looks like this:

  1. Write clear, predictable React code.
  2. Enable React Compiler.
  3. Measure real performance bottlenecks.
  4. Add useMemo, useCallback, or React.memo only where evidence supports it.

That approach generally produces code that is easier to read, maintain, and optimize over time.

React Compiler setup

React Compiler runs through your build tool.

Next.js

First, install the compiler package:

npm install -D babel-plugin-react-compiler

Then enable React Compiler in next.config.js:

// next.config.js
const nextConfig = {
  reactCompiler: true,
};
export default nextConfig;

Vite

For Vite, React Compiler setup requires installation because the compiler runs through Babel integration.

Install the required dev dependencies:

npm install -D babel-plugin-react-compiler @rolldown/plugin-babel

Then configure React Compiler in your Vite setup according to the React documentation and the version of @vitejs/plugin-react used in your project.

The exact configuration can vary between releases, so always verify against the latest React documentation before enabling it in production.

Frequently Asked Questions

How do I know if React Compiler skipped a component?

React Compiler only optimizes code it can safely analyze. Components that violate React rules, contain mutations, or depend on unsupported patterns may be skipped. React’s ESLint rules are the easiest way to identify potential issues before they become performance problems.

Can I use React Compiler with React 18?

Yes. Although React Compiler is designed to work best with React 19, it supports React 17 and React 18 through additional runtime and compiler configuration. Always review the latest React documentation for current compatibility and setup requirements before enabling it in production.

Will React Compiler fix slow API calls?

No. React Compiler optimizes rendering work. It won’t reduce network latency, speed up database queries, or improve oversized API responses.

If your application is slow because data arrives slowly, performance work should focus on caching, pagination, streaming, and backend architecture instead.

Should I Still Profile React Applications?

Absolutely. React Compiler improves the baseline, but profiling remains the only reliable way to identify real bottlenecks.

Use React DevTools Profiler to understand render behavior and browser performance tools to investigate scripting, layout, paint, and interaction delays.

Explore the endless possibilities with Syncfusion’s outstanding React UI components.

Conclusion

React Compiler changes one of the longest-running habits in React development.

For years, many developers reached for useMemo, useCallback, and React.memo before they knew whether performance was actually a problem. Today, React Compiler can automatically handle many of those routine optimizations when it can safely analyze the relevant code.

That doesn’t make manual memoization obsolete. It simply changes when you should use it.

The new default is straightforward:

  1. Write readable React code.
  2. Let the compiler optimize safe cases.
  3. Measure performance.
  4. Add manual memoization only when the data proves it’s necessary.

In 2026, useMemo, useCallback, and React.memo are still valuable tools. They’re just no longer the first tools you reach for.

Be the first to get updates

Arunachalam Kandasamy RajaArunachalam Kandasamy Raja profile icon

Meet the Author

Arunachalam Kandasamy Raja

Arunachalam Kandasamy Raja is a software developer working with Microsoft technologies since 2022. He specializes in developing custom controls and components designed to improve application performance and usability. He is also actively exploring artificial intelligence and large language models to understand how AI-driven technologies can shape the future of modern software development.

Leave a comment