aidenybai / react-scan

Scan for React performance issues and eliminate slow renders in your app
https://react-scan.million.dev
MIT License
1.73k stars 12 forks source link
javascript react react-dom react-scan rendering

React Scan

React Scan detects performance issues in your React app.

Previously, tools like <Profiler />, Why Did You Render?, and React Devtools required lots of manual code change, lacked simple visual cues, and had noisy data.

Instead, React Scan automatically detects and highlights renders that cause performance issues. This shows you exactly which components you need to fix.

It's also just JavaScript, so you drop it in anywhere – script tag, npm, you name it!

Try it out! →

React Scan in action

Looking for a more advanced version? Check out Million Lint!

Install

Get started in 5 seconds, add this script to your app:

<!-- import this BEFORE any scripts -->
<script src="https://unpkg.com/react-scan/dist/auto.global.js"></script>

Examples:

Next.js (pages)
Add the script tag to your `pages/_document.tsx`: ```jsx import { Html, Head, Main, NextScript } from 'next/document'; export default function Document() { return ( {/* rest of your scripts go under */}
); } ```
Next.js (app)
Add the script tag to your `app/layout.tsx`: ```jsx export default function RootLayout({ children, }: { children: React.ReactNode }) { return ( ```

Or, install it via npm:

npm install react-scan

Then, in your app, import this BEFORE react:

import { scan } from 'react-scan'; // import this BEFORE react
import React from 'react';

scan({
  enabled: true,
  log: true, // logs render info to console (default: false)
  clearLog: false, // clears the console per group of renders (default: false)
});

API Reference

scan(options)
Automatically scan your app for renders. ```jsx scan({ /** * Enable/disable scanning */ enabled: true, /** * Include children of a component applied with withScan */ includeChildren: true, /** * Run in production */ runInProduction: false, /** * Enable/disable geiger sound */ playSound: true, /** * Log renders to the console */ log: false, /** * Show toolbar bar */ showToolbar: true, /** * Long task threshold in milliseconds, only show * when main thread is blocked for longer than this */ longTaskThreshold: 50; /** * Clear aggregated fibers after this time in milliseconds */ resetCountTimeout: 5000; onCommitStart?: () => void; onRender?: (fiber, render) => void; onCommitFinish?: () => void; onPaintStart?: (outline) => void; onPaintFinish?: (outline) => void; }) ```
withScan(Component, options)
Scan a specific component for renders. ```jsx function Component(props) { // ... } withScan(Component, { /** * Enable/disable scanning */ enabled: true, /** * Include children of a component applied with withScan */ includeChildren: true, /** * Run in production */ runInProduction: false, /** * Enable/disable geiger sound */ playSound: true, /** * Log renders to the console */ log: false, /** * Show toolbar bar */ showToolbar: true, /** * Long task threshold in milliseconds, only show * when main thread is blocked for longer than this */ longTaskThreshold: 50; /** * Clear aggregated fibers after this time in milliseconds */ resetCountTimeout: 5000; onCommitStart?: () => void; onRender?: (fiber, render) => void; onCommitFinish?: () => void; onPaintStart?: (outline) => void; onPaintFinish?: (outline) => void; }) ```
getReport()
Get a aggregated report of all components and renders. ```jsx const report = getReport(); for (const component in report) { const { count, time } = report[component]; console.log(`${component} rendered ${count} times, took ${time}ms`); } ```
setOptions(options) ```jsx function Component(props) { // ... } setOptions({ /** * Enable/disable scanning */ enabled: true, /** * Include children of a component applied with withScan */ includeChildren: true, /** * Run in production */ runInProduction: false, /** * Enable/disable geiger sound */ playSound: true, /** * Log renders to the console */ log: false, /** * Show toolbar bar */ showToolbar: true, /** * Long task threshold in milliseconds, only show * when main thread is blocked for longer than this */ longTaskThreshold: 50; /** * Clear aggregated fibers after this time in milliseconds */ resetCountTimeout: 5000; onCommitStart?: () => void; onRender?: (fiber, render) => void; onCommitFinish?: () => void; onPaintStart?: (outline) => void; onPaintFinish?: (outline) => void; }) ```
getOptions() ```jsx const { enabled, includeChildren, runInProduction, playSound, log, showToolbar, longTaskThreshold, resetCountTimeout, } = getOptions(); ```

Why React Scan?

React can be tricky to optimize.

The issue is that component props are compared by reference, not value. This is intentional – this way rendering can be cheap to run.

However, this makes it easy to accidentally cause unnecessary renders, making the the app slow. Even in production apps, with hundreds of engineers, can't fully optimize their apps (see GitHub, Twitter, and Instagram).

This often comes down to props that update in reference, like callbacks or object values. For example, the onClick function and style object are re-created on every render, causing ExpensiveComponent to slow down the app:

<ExpensiveComponent onClick={() => alert('hi')} style={{ color: 'purple' }} />

React Scan helps you identify these issues by automatically detecting and highlighting renders that cause performance issues. Now, instead of guessing, you can see exactly which components you need to fix.

Want to automatically fix these issues? Check out Million Lint!

FAQ

Q: Why this instead of React Devtools?

React Devtools aims to be a general purpose tool for React. However, I deal with React performance issues every day, and React Devtools doesn't fix my problems well. There's a lot of noise (no obvious distinction between unnecessary and necessary renders), and there's no programmatic API. If it sounds like you have the same problems, then React Scan may be a better choice.

Also, some personal complaints about React Devtools' highlight feature:

Q: React Native wen?

Soon :)

Q: Chrome Extension wen?

Soon :)

Resources & Contributing Back

Want to try it out? Check the our demo.

Looking to contribute back? Check the Contributing Guide out.

Want to talk to the community? Hop in our Discord and share your ideas and what you've build with React Scan.

Find a bug? Head over to our issue tracker and we'll do our best to help. We love pull requests, too!

We expect all contributors to abide by the terms of our Code of Conduct.

→ Start contributing on GitHub

Roadmap

Acknowledgments

React Scan takes inspiration from the following projects:

License

React Scan is MIT-licensed open-source software by Aiden Bai, Million Software, Inc., and contributors: