React Compiler Essentials
React Compiler is a build-time optimization tool that handles component re-rendering and memoization automatically, so you can get optimal performance without complex manual optimization.
You no longer need to use the useMemo and useCallback hooks or the memo function.
It's officially supported from React v19, and with extra configuration you can also use it in React v17 and v18.
# Before the compiler
Before React Compiler, to optimize re-rendering, you had to manually memoize components, functions, and values with React.memo, useCallback, and useMemo.
This prevents unnecessary re-renders and extra computation, but it hurts code readability and makes you write more complex code.
12345678910111213141516171819202122232425262728import { memo, useMemo } from 'react' function Counter({ count, onIncrease }: { count: number onIncrease: () => void }) { console.log('Counter component rendered!') // Memoize the computed value with useMemo const double = useMemo(() => { console.log('double value computed!') return count * 2 }, [count]) return ( <div> <p>Count: {count}</p> <p>Double: {double}</p> <button onClick={onIncrease}>Increase!</button> </div> ) } // Memoize the component with memo export default memo(Counter)
123456789101112131415161718192021222324252627import { useState, useCallback } from 'react' import Counter from '@/components/Counter' export default function App() { const [text, setText] = useState('') const [count, setCount] = useState(0) // Memoize the function with useCallback const increase = useCallback(() => { console.log('increase function called!') setCount(c => c + 1) }, []) return ( <> <input placeholder="Type anything!" value={text} onChange={e => setText(e.target.value)} /> <Counter count={count} onIncrease={increase} /> </> ) }
# After the compiler
With React Compiler, your code is optimized even beyond what you'd get by writing manual memoization yourself.
When state changes, only the parts that need it are re-rendered or recomputed, which is also called fine-grained reactivity.
123456789101112131415161718192021222324function getDouble(count: number) { console.log('double value computed!') return count * 2 } export default function Counter({ count, onIncrease }: { count: number onIncrease: () => void }) { console.log('Counter component rendered!') const double = getDouble(count) return ( <> <p>Count: {count}</p> <p>Double: {double}</p> <button onClick={onIncrease}>Increase!</button> </> ) }
1234567891011121314151617181920212223242526import { useState } from 'react' import Counter from '@/components/Counter' export default function App() { const [text, setText] = useState('') const [count, setCount] = useState(0) const increase = () => { console.log('increase function called!') setCount(c => c + 1) } return ( <> <input placeholder="Type anything!" value={text} onChange={e => setText(e.target.value)} /> <Counter count={count} onIncrease={increase} /> </> ) }
# Using it in a new project
If you create a new project with the Vite build tool or Next.js, you can use React Compiler very easily.
Vite setup
12345npm create vite@latest . ◇ Select a framework: React ◇ Select a variant: TypeScript + React Compiler # ...
Next.js setup
1234567npx create-next-app@latest . ✔ Would you like to use the recommended Next.js defaults? › No, customize settings ✔ Would you like to use TypeScript? … No / Yes ✔ Which linter would you like to use? › ESLint ✔ Would you like to use React Compiler? … No / Yes # ...
# Using it in an existing project
Let's look at the steps needed to use React Compiler in an existing project.
It's a good idea to adopt it gradually: test it on small parts first, make sure it's stable, and then expand it to the whole project.
First, install the required package, babel-plugin-react-compiler, as follows.
1npm i -D babel-plugin-react-compiler
Vite setup
In the vite.config.ts configuration file, add babel-plugin-react-compiler as a Babel plugin in the React options.
You can simply specify the plugin name to use React Compiler with its defaults, or add compiler options for gradual adoption.
1234567891011121314151617import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' export default defineConfig({ plugins: [ react({ babel: { plugins: [ // 'babel-plugin-react-compiler' // or ['babel-plugin-react-compiler', { // Compiler options.. }] ] } }) ] })
Next.js setup
In Next.js v15.3.1 and later, you can use React Compiler easily.
In the next.config.ts configuration file, enable the SWC-based React Compiler with the reactCompiler option.
12345678910import type { NextConfig } from 'next' const nextConfig: NextConfig = { // reactCompiler: true // or reactCompiler: { // Compiler options.. } } export default nextConfig
# React 17/18 support
With extra configuration, you can use React Compiler in React v17 or v18 too.
Install the react-compiler-runtime package and specify the React version you use with the target compiler option.
This lets the compiler use the correct API implementation for your React version or polyfill missing APIs.
1npm i react-compiler-runtime
1234567{ plugins: [ ['babel-plugin-react-compiler', { target: '17' }] ] }
12345{ reactCompiler: { target: '17' } }
# ESLint integration
You can use ESLint rules that help you identify code that React Compiler can't optimize.
Configure the recommended rule preset of the eslint-plugin-react-hooks ESLint plugin.
1npm i -D eslint-plugin-react-hooks@^7
123456789import reactHooks from 'eslint-plugin-react-hooks' export default defineConfig([ { extends: [ reactHooks.configs.flat.recommended ] } ])
# Directory-based
Babel's overrides option lets you apply different plugins to different parts of your codebase.
You can start with a specific directory and expand gradually.
Vite setup
12345678910111213141516171819import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' export default defineConfig({ plugins: [react({ babel: { overrides: [ { test: [/src\/features\/.*\.(js|jsx|ts|tsx)$/], plugins: [ ['babel-plugin-react-compiler', { target: '17' }] ] } ] } })] })
# Opt-out based
React Compiler automatically identifies and compiles components and hooks that follow React's naming rules.
If you don't want some components or hooks to be compiled for testing purposes, you can exclude them with the 'use no memo' directive, and this is called opting out.
The directive must be used at the top level of the component or hook function.
12345export default function Counter() { 'use no memo' // ... }
# Opt-in based
If you don't want to compile anything except the few components or hooks you want to test, you can use the 'use memo' directive.
Unlike the 'use no memo' directive, however, compiling only the code that has the directive requires the compilationMode: 'annotation' option.
This is called opting in.
1234567{ plugins: [ ['babel-plugin-react-compiler', { compilationMode: 'annotation' }] ] }
Add the 'use memo' directive to the components or hooks you want to compile.
12345export default function Counter() { 'use memo' // ... }
Thanks for reading to the end.
Likes and kind comments help keep this blog going!