Design Converter
Education
Last updated on Feb 27, 2025
•22 mins read
Last updated on Feb 26, 2025
•22 mins read
React applications can suffer from subtle performance issues when components re-render more often than necessary. React Scan is a powerful open-source tool (loved by engineers at Airbnb and Perplexity) that automatically detects such performance problems in your React app and highlights exactly which components need optimization (GitHub ).
In this article, we'll explore React Scan, how it helps identify inefficient renders, and how to integrate it into your development workflow. You'll learn to use its API for targeted diagnostics, apply optimizations like React.memo, and follow best practices to boost your app's performance.
Let's dive in!
React Scan is a performance diagnostic tool for React applications that “automatically detects performance issues in your React app”. It addresses pain points that developers faced with earlier tools. Previously, identifying wasted renders often required manual changes or guesswork – for example, using the React Profiler demanded inserting <Profiler>
components, the popular "why-did-you-render" library lacked easy visual cues, and React DevTools offered no simple programmatic API. React Scan solves these problems by providing:
• Zero Code Changes: You can enable it without modifying your components – “just drop it in”. No need to wrap components or write special debug code.
• Visual Highlighting: It “highlights exactly the components you need to optimize”. Problematic components are outlined in the browser, so you can spot performance bottlenecks at a glance.
• Flexible Integration: It's available via script tag, npm package, CLI, and even a browser extension – “you name it!”. This portability means you can use React Scan in any React environment with minimal setup.
At its core, React Scan hooks into React’s rendering process to monitor component updates. It keeps track of when each component renders and can tell whether a re-render was actually needed. In React, component props and state are compared by reference, not by value, which means even an unchanged object prop (or an inline function) will cause a child component to re-render. This intentional design makes updates fast, but also “makes it easy to accidentally cause unnecessary renders, making the app slow”. React Scan shines a light on these inefficiencies. It detects when a component re-renders without any real change to the DOM output – what it defines as an unnecessary render (a re-render “with no change to the component's corresponding DOM subtree”). By catching these cases, React Scan helps developers focus on true problem areas instead of guessing.
To understand this concept, consider a parent component that passes an inline callback or a newly created style object to a child on every render. Each time the parent updates, it recreates those props, causing the child to re-render even though the rendered output stays the same. For example:
1// Parent component render (inefficient pattern) 2<ExpensiveComponent 3 onClick={() => alert("Hi")} 4 style={{ color: "purple" }} 5/>
In this snippet, the ExpensiveComponent will re-render on every parent update because its onClick prop (a new function) and style prop (a new object) change by reference each time. Such re-renders can slow down your app. React Scan will catch this by highlighting ExpensiveComponent as needing attention. Now, instead of digging through profiling charts, you get an instant visual cue about the culprit.
Diagram: How React Scan distinguishes necessary vs. unnecessary renders. In the above flow, React Scan checks each rendering component. If a component's output hasn't changed (an unnecessary render), it flags that component with a highlight, alerting the developer to optimize it. This removes the guesswork from performance tuning – you can “see exactly which components you need to fix”.
React Scan directly aids in performance optimization by identifying inefficient render patterns. It not only notes when components render, but can also differentiate between necessary and unnecessary renders. A necessary render is one that actually updates the UI (due to changed state or props), whereas an unnecessary render is wasted work – the component re-drew itself but the output didn't change. With React Scan's tracking, such wasted renders are easy to spot. In fact, if you enable the trackUnnecessaryRenders option, React Scan will mark unnecessary renders with a distinct gray outline, making them immediately apparent during development.
Why is this distinction so important? Every unnecessary render consumes CPU time and can contribute to slowdowns, especially in large applications. It's not uncommon for production apps (even at companies like GitHub or Twitter) to harbor these inefficiencies. React Scan shows you these performance bottlenecks live, as you interact with your app. For example, if clicking a button causes a dozen components to flash updates but only a few of them actually needed to update, React Scan will highlight the ones that didn't need to re-render. This level of insight is typically hard-won through manual profiling sessions; with React Scan it's automatic.
By visualizing render behavior, React Scan helps developers focus optimization efforts precisely. You no longer have to wonder which components are causing slowdowns – the tool points them out for you. This means you can apply fixes like memoization or state refactoring exactly where they're needed, rather than prematurely optimizing everything. In other words, React Scan enables targeted performance tuning. This not only saves developer time but also ensures you address the real bottlenecks that affect users.
In practice, using React Scan might reveal, say, a list item component rendering hundreds of times when only a few items changed, or a context provider causing re-renders across the app. Once identified, you can apply React's optimization patterns (like React.memo, useMemo, or lifting state up) to eliminate the unnecessary work. Because React Scan updates in real-time, you can immediately see the impact of your optimizations – the previously highlighted components will no longer flash, confirming that those renders are fixed. This feedback loop makes React Scan a must-have tool for any React developer serious about performance: it turns performance tuning from a guessing game into a guided, efficient process.
One of React Scan's strengths is how easy it is to integrate into your workflow. You have multiple options to set it up, depending on your use case:
• Via NPM (or Yarn/Pnpm/etc): Add React Scan as a dependency to your project. For example, using npm: npm i react-scan
(or yarn add react-scan
, etc.). Once installed, you can import its API in your code. This method is great for apps where you want React Scan running during development as part of the app itself.
• Via Script Tag: If you prefer zero build configuration, you can include React Scan through a CDN. Simply drop a script tag before any other scripts on your page to activate it. For example, add to your HTML:
1<!-- Include React Scan before your app's scripts --> 2<script src="<https://unpkg.com/react-scan/dist/auto.global.js>" crossOrigin="anonymous"></script>
This one-liner (loaded from Unpkg) will automatically hook into React when your app runs. No code changes required – just open your app in a browser after adding the script, and the React Scan toolbar/overlays will appear.
• Using the CLI: React Scan provides a command-line interface for scanning any React application, even remotely. This is perfect for quick audits or if you don't want to modify the app at all. With NPX, you can run:
1npx react-scan@latest <http://localhost:3000>
This will launch an interactive browser window and start scanning the app hosted at that URL. You can even point it at public sites – “you can technically scan ANY website on the web”, as long as it's a React app. (For instance: npx react-scan@latest <https://react.dev>
will scan React's official site.)
You can integrate the CLI into your dev process. For example, in a Next.js project you might add a script to your package.json that runs your dev server and React Scan together:
1{ 2 "scripts": { 3 "dev": "next dev", 4 "scan": "next dev & npx react-scan@latest <http://localhost:3000>" 5 } 6}
Running npm run scan
in development would start Next.js on port 3000 and open React Scan to analyze it.
• Browser Extension: For an even more seamless experience, React Scan is also available as a browser extension. This lets you analyze any React app directly in the browser without running additional commands or altering the app. The extension adds a React Scan toolbar to pages, similar to React DevTools. (As of this writing, the extension is pending approval in Chrome/Firefox stores, but can be installed manually.) Using the extension, you could visit your app (or any React-based site) and immediately see highlights of renders, making it a convenient option for quick checks and for non-developers on the team to visualize performance issues.
• Framework-specific Setup: React Scan works with popular frameworks and build tools out-of-the-box. Whether your project is in Next.js, Create React App, Vite, Remix, or others, you can use React Scan. In Next.js (App Router), for example, you can create a small client component that calls scan()
on mount and include it at the top of your root layout. The key requirement is that React Scan's code runs before React mounts your components (hence the recommendation to put the script at the very top of <head>
or import it before React in code). Detailed installation guides are provided for various setups (Next.js App Router, Next.js Pages Router, CRA, Vite, etc.) in the React Scan documentation.
Regardless of which method you choose, the setup is straightforward. In most cases, it’s a one-liner addition. Once React Scan is included and running (typically only in development), you'll notice a small toolbar overlay in your app and see components getting outlined when they render. Now you're ready to leverage its API and start hunting down performance issues.
React Scan provides a simple API that you can use to control scanning and gather information programmatically. These APIs give developers fine-grained control beyond the automatic highlighting. Key APIs include:
• scan(options) – Imperative function to start scanning with the given options. You can call scan()
in your code (usually at app startup or in a top-level component) to enable React Scan. If you've included the auto script, this is handled for you, but when using the import, you'll call this once. For example:
1import { scan } from 'react-scan'; 2scan({ enabled: true });
This would initialize React Scan. You typically guard this so it runs only in development.
• useScan(options) – A React Hook that starts scanning when used inside a component. This provides a convenient way to activate React Scan in functional components. For instance, in a Next.js or CRA app, you might call useScan()
in your <App>
component or root layout component. It behaves similarly to calling scan(...)
inside a useEffect
. Using the hook ensures scanning begins as part of the React render cycle. (If you only want to scan a specific subtree or conditionally start scanning, useScan
can be handy.)
• getReport() – Retrieves a report of all renders captured so far. This returns an aggregated dataset of render information. You can use it to generate logs or analytics about rendering frequency and find out which components rendered how many times. For example, after some user interactions, const report = getReport()
will give you data on each component's renders. This is useful for offline analysis or testing. The report can show you all components that rendered, timestamps, and whether renders were flagged unnecessary, enabling you to verify that performance issues have been resolved.
• setOptions(options) – Adjusts React Scan's configuration on the fly. This allows you to change settings (the same ones you can pass to scan
) at runtime. For instance, you could toggle log or trackUnnecessaryRenders based on some UI control or debug flag in your app by calling setOptions({ log: true })
.
• getOptions() – Fetches the current configuration options React Scan is using. This can be used to check if scanning is enabled or what settings are active (e.g., to ensure trackUnnecessaryRenders is on when you expect it).
• onRender(Component, callback) – Hooks into a specific component’s render events. This API is more advanced: it lets you register a callback that runs whenever a particular component renders. For example, onRender(MyComponent, (fiber, render) => { ... })
will call your function every time MyComponent renders, giving you access to the fiber info and render details. This is useful if you want to programmatically track or log render timings for a critical component, or trigger custom analytics when certain components update.
All of these APIs accept or return an Options configuration object in some way. The Options interface defines various settings that control React Scan's behavior. Key options include:
• enabled (boolean): Master switch to turn scanning on or off. By default it's true in development. The docs recommend setting this to run only in dev mode (e.g., enabled: process.env.NODE_ENV === 'development'
) so that React Scan automatically disables itself in production builds.
• dangerouslyForceRunInProduction (boolean): As the name suggests, this will force React Scan to run even in production if set to true. This is not recommended for normal use due to performance overhead, but it can be useful if you need to debug performance on a production build or in a staging environment. By default it's false (disabled).
• log (boolean): If true, React Scan will log every render to the console. This can give you a detailed trace of component render timings and hierarchy in text form. However, use this sparingly – logging every render can slow things down significantly, especially if your app re-renders frequently. It’s off (false) by default.
• showToolbar (boolean): Controls whether the React Scan toolbar UI is shown. The toolbar provides an on/off toggle and possibly other info. If you prefer to run React Scan silently or control it via code only, you might hide the toolbar by setting this to false. (By default it's true so you get the UI.)
• animationSpeed ("slow" | "fast" | "off"): Adjusts the speed of the highlight animations (). When components render, React Scan highlights them (e.g., outlines flash). You can slow down the animation if you need more time to see it, or turn it off for instant/static outlines.
• trackUnnecessaryRenders (boolean): If enabled, React Scan will actively detect unnecessary renders and mark them distinctly (with gray outline). This is off by default. Turning it on incurs some overhead because React Scan has to compare previous and next DOM output for each component render to decide if it truly changed. When on, it gives you the powerful insight of which renders were essentially no-ops. Use this when you specifically want to hunt down wasted renders, and possibly turn it off when focusing on general profiling to reduce overhead.
In most cases, you won't need to call many of these functions yourself – simply dropping in React Scan will start highlighting issues. But the APIs are there for deeper integration and analysis. For example, you might generate a summary report in your test suite by running a scenario and then calling getReport()
to ensure no component rendered more than expected. Or you might use onRender
during development to keep a count of how often a specific component renders, beyond just visual cues.
Typically, a simple usage in a dev environment might look like this:
1import { scan } from 'react-scan'; 2 3// Start React Scan with recommended dev-only setting and unnecessary render tracking 4scan({ 5 enabled: process.env.NODE_ENV === 'development', 6 trackUnnecessaryRenders: true 7});
This initializes React Scan only if in development mode and turns on unnecessary render tracking for maximum insight. Once this is run (e.g., at app startup), you can interact with your app and watch React Scan do its magic. Components will get outlined when they render – usually a quick flash of color – so you can literally see the rendering dynamics of your app in real time.
The true power of React Scan is that it tells you where to focus your optimization efforts. Instead of preemptively optimizing everything, you can optimize exactly the components that React Scan highlights as problematic. Here are some practical strategies and examples for tuning component performance based on React Scan's insights:
• Prevent Unnecessary Re-renders with React.memo:
◦ If React Scan indicates a child component is re-rendering frequently without prop changes (often shown by grey outlines if trackUnnecessaryRenders is on), consider wrapping that component in React.memo
. React.memo is a higher-order component that memoizes the rendered output.
◦ React will skip re-rendering that component if its props are the same as last time. For example, if ExpensiveList is rendering repeatedly due to parent updates, you can export it as export default React.memo(ExpensiveList);
. Now it will only re-render when its props actually change. This one change could eliminate a whole class of unnecessary renders. Keep in mind that React.memo does a shallow comparison of props – so to be effective, ensure that the props you pass are either primitives or stable references.
◦ In cases like our earlier ExpensiveComponent example (where a new function or object was passed each time), you should also memoize those props (e.g., use useCallback
for the function or define the style object outside the render or with useMemo
). By using React.memo and stable props, you align with React’s default comparison-by-reference behavior and avoid triggering renders from props that haven't logically changed.
• Leverage getReport() to validate improvements: After applying optimizations, you might want to confirm that they worked. Visually, you should see fewer highlights from React Scan. But for a more quantitative check, call getReport()
in the console or in code. The report will list all components and renders. You can inspect it to ensure that the previously noisy component now has a much lower render count or zero unnecessary renders. For example:
1import { getReport } from 'react-scan'; 2// ... after interacting with the app ... 3const report = getReport(); 4console.log(report);
This might output something like:
1{ 2 "ExpensiveList": { renders: 5, unnecessary: 0 }, 3 "ExpensiveListItem": { renders: 5, unnecessary: 0 }, 4 "NavBar": { renders: 1, unnecessary: 0 }, 5 // ...etc 6}
(The exact structure may vary). From this, you can verify that ExpensiveList rendered 5 times (perhaps expected if you clicked 5 times) and none of those were unnecessary. If you had run this before optimization, you might have seen unnecessary: 5
for ExpensiveListItem, for example. Using getReport()
in this way turns React Scan into a reporting tool, not just a visual aid, letting you confidently measure the effect of your changes.
• Fix root causes of re-renders: React Scan identifies symptoms (extra renders), but it's up to you to fix the cause. Common fixes include:
◦ Memoizing child components (React.memo) as discussed.
◦ Using useCallback
or useMemo
in a parent to avoid changing prop references each render (e.g., memoize callback props or expensive computations).
◦ Splitting a large component into smaller components so that only the part of the UI that actually changed gets re-rendered.
◦ Removing or refactoring any unnecessary state or context updates that cause broad re-renders.
React Scan basically creates a feedback loop: highlight -> optimize -> verify. Each iteration makes your app more efficient. For example, if a context provider is causing many components to update, you might refactor context or use selectors to limit updates, then see the highlights disappear for unaffected components.
In summary, React Scan ensures you only spend time optimizing where it truly matters. By following its cues, you might wrap a few components in memo, adjust a couple of prop declarations, and perhaps refactor one context – and see dramatic improvements – rather than blindly optimizing everything. This targeted approach is both time-saving and effective.
To get the most out of React Scan, consider the following best practices in your implementation:
• Use React Scan in Development Only:
◦ React Scan is designed as a development diagnostic tool. You should enable it when working on performance in dev/test environments, but not ship it to production users. The recommended pattern is to tie its activation to the environment (e.g., enabled: process.env.NODE_ENV === 'development'
when calling scan()
) .
◦ This ensures no accidental performance overhead in production. The dangerouslyForceRunInProduction
flag exists for edge cases, but generally keep it off.
• Load React Scan Before React: For React Scan to intercept renders, it must be initialized before your React app mounts. If using the CDN script, put it at the very top of the HTML (above your bundle). If using imports, make sure to import/execute React Scan setup code before any React code. In Next.js App Router, for example, the docs show importing the scan()
call in a component placed at the top of your <html>
in the root layout. Not following this order might result in React Scan not catching some initial renders.
• Avoid Performance Side-Effects: While React Scan itself is lightweight, certain options like log
and trackUnnecessaryRenders
add overhead. Use them when needed, but consider turning them off when you don’t require that info. For instance, keep trackUnnecessaryRenders
off until you are specifically investigating wasted renders (as it “can add meaningful overhead”). Similarly, leaving log: true
will flood your console and slow things down if many renders occur. Use the toolbar or visual cues for high-level info, and only resort to heavy logging or tracking for deep dives.
• Integrate with Frameworks Properly: Each framework may have a slightly different optimal integration point. For Next.js (Pages Router), inserting scan()
in a custom _app.js
inside a useEffect
works well. For CRA or Vite, you can call scan()
in your index.js
before calling ReactDOM.render
. Follow the official guides for your setup to ensure React Scan initializes correctly. In SSR scenarios, ensure it's only running on the client side (you might guard the import or call with a check like typeof window !== 'undefined'
if necessary to avoid running on the server).
• Leverage the Browser Extension for External Analysis: If you want to analyze a production deployment or a third-party React app without modifying its code, the React Scan browser extension is invaluable. Use it to audit performance of pages on the fly. This can be great for competitor analysis or debugging an issue on a live site without redeploying instrumentation. Just remember, the extension will only highlight what it sees – it can't run getReport()
for you in the page context, for example. For deeper analysis on live sites, the CLI might be used on a staging URL where you can still call getReport()
via the console.
• Combine with Other Tools Thoughtfully: React Scan is complementary to React DevTools Profiler. You might use React Scan to find where unnecessary renders happen, then use the Profiler to dig into why a particular component is slow (e.g., CPU time taken by rendering). Also consider using React's built-in memo and useMemo
as first-class solutions to issues React Scan surfaces – treat React Scan’s output as guidance for applying those React features effectively.
• Regularly Monitor Performance: It's a good practice to run React Scan at intervals during development, especially after introducing new features or refactoring. This helps catch performance regressions early. Some teams even incorporate performance checks into their CI pipelines – for example, using the CLI in a headless mode to ensure a certain interaction doesn't blow past a render count threshold. Whether manually or automated, make React Scan a part of your testing checklist for any significant UI change.
• Cleanup Before Production: If you've added React Scan via script tag or imports, make sure to remove or disable it for production builds. This might mean having an environment variable to exclude the script, or a build step to drop any import 'react-scan'
calls. Although React Scan won't run if enabled
is false, it's best practice not to include unnecessary libraries in production bundles at all.
By following these best practices, you ensure that React Scan helps you during development without any downsides in production. It also makes your use of the tool more effective and seamless.
React Scan has gained significant adoption in the developer community and is used by teams at companies of various sizes to keep their React apps performant. In fact, it is “trusted by engineering teams at Airbnb” and has been utilized by developers at organizations like Shopify, Perplexity, and Faire to name a few. These companies have large, complex React applications where even small inefficiencies can impact users. By integrating React Scan into their workflow, they can automatically catch regressions in rendering behavior and ensure new code doesn't introduce avoidable re-renders.
For example, at Airbnb, with its vast web application, React Scan can help surface components that might be doing redundant work on pages like search results or listing details. An engineer making changes to the reservation widget might run React Scan and discover that a certain date picker component re-renders too often.
With that knowledge, they could memoize the date picker or refactor context usage, resulting in a smoother experience for end users. Similar stories play out at Shopify, where merchant dashboards and storefronts are highly dynamic – React Scan can pinpoint, say, a product card component that updates more than necessary when inventory data refreshes, guiding developers to optimize it.
Open-source developers and maintainers also leverage React Scan to improve their libraries. With over 15,000 stars on GitHub, React Scan has a broad user base contributing to its evolution. The community shares use cases like using React Scan to debug performance issues in data-heavy apps or to educate newer developers about React rendering patterns (it's very visually instructive to see what triggers renders). The tool has even inspired further innovation; it's part of the suite of performance tools by its author (Aiden Bai of Million.js fame), and there is a React Scan Monitoring service for capturing these insights in production scenarios.
Tired of manually designing screens, coding on weekends, and technical debt? Let DhiWise handle it for you!
You can build an e-commerce store, healthcare app, portfolio, blogging website, social media or admin panel right away. Use our library of 40+ pre-built free templates to create your first application using DhiWise.