Add guide for performant charts#6659
Conversation
WalkthroughIntroduces a new Performance guidance page for Recharts with code examples demonstrating optimization patterns like component isolation, stable references, and throttling. Updates routing, navigation data, and localization to include the new guide page in English and Chinese. Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes
Possibly related PRs
Suggested reviewers
Pre-merge checks and finishing touches❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Tip 📝 Customizable high-level summaries are now available in beta!You can now customize how CodeRabbit generates the high-level summary in your pull requests — including its content, structure, tone, and formatting.
Example instruction:
Note: This feature is currently in beta for Pro-tier users, and pricing will be announced later. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
BTW my experience with performance optimization was that I installed the eslint plugin (I described it in the guide too), then asked AI to run lint and fix everything, ... and that was it. It's all pretty standard and at least 4.5 Sonnet fixed it all just fine and the chart was fast. |
There was a problem hiding this comment.
Actionable comments posted: 3
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
www/test/__snapshots__/navigation.spec.ts.snapis excluded by!**/*.snap
📒 Files selected for processing (6)
www/src/components/GuideView/Performance/index.tsx(1 hunks)www/src/locale/en-US.ts(1 hunks)www/src/locale/zh-CN.ts(1 hunks)www/src/navigation.data.ts(1 hunks)www/src/utils/CodeMirrorEditor.tsx(2 hunks)www/src/views/GuideView.tsx(2 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: PavelVanecek
Repo: recharts/recharts PR: 6640
File: src/cartesian/Bar.tsx:156-159
Timestamp: 2025-11-16T09:14:24.891Z
Learning: In recharts, SSR (Server-Side Rendering) is not yet supported—charts don't render at all in SSR environments. The `isAnimationActive: 'auto'` mode is preparatory work for future SSR support, so testing of this mode should be deferred until SSR support is actually implemented.
📚 Learning: 2025-11-16T09:14:24.891Z
Learnt from: PavelVanecek
Repo: recharts/recharts PR: 6640
File: src/cartesian/Bar.tsx:156-159
Timestamp: 2025-11-16T09:14:24.891Z
Learning: In recharts, SSR (Server-Side Rendering) is not yet supported—charts don't render at all in SSR environments. The `isAnimationActive: 'auto'` mode is preparatory work for future SSR support, so testing of this mode should be deferred until SSR support is actually implemented.
Applied to files:
www/src/components/GuideView/Performance/index.tsx
🧬 Code graph analysis (2)
www/src/views/GuideView.tsx (1)
www/src/components/GuideView/Performance/index.tsx (1)
Performance(169-318)
www/src/components/GuideView/Performance/index.tsx (1)
www/src/utils/CodeMirrorEditor.tsx (1)
CodeMirrorEditor(90-258)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Build, Test, Pack
- GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (17)
www/src/locale/en-US.ts (1)
45-45: LGTM!The translation entry for the new Performance guide is correctly added and consistent with the existing locale structure.
www/src/locale/zh-CN.ts (1)
45-45: LGTM!The Chinese translation for the Performance guide is correctly added and mirrors the English locale structure.
www/src/navigation.data.ts (1)
11-20: LGTM!The new 'performance' route is correctly added to the guide pages array, and the reformatting to a multi-line structure improves readability.
www/src/views/GuideView.tsx (2)
10-10: LGTM!The Performance component import follows the established pattern for guide pages.
20-20: LGTM!The Performance component is correctly registered in the guide map, enabling routing to the new performance guide page.
www/src/utils/CodeMirrorEditor.tsx (2)
82-82: LGTM! Note the behavioral change.The new helper function is well-named and correctly removes both leading and trailing newlines (previously only trailing newlines were removed). This improves the display of code examples by eliminating unnecessary whitespace.
103-103: LGTM!The application of the new trimming function is correct and improves code presentation by removing both leading and trailing newlines.
www/src/components/GuideView/Performance/index.tsx (10)
4-32: LGTM!The "bad example" clearly demonstrates how root-level state changes cause unnecessary re-renders of the entire chart. The code is pedagogically sound and easy to understand.
34-72: LGTM!The "good example" effectively demonstrates component isolation to prevent unnecessary re-renders. The HoverableLine component pattern is a practical solution that users can apply to their own code.
74-94: LGTM!The example effectively demonstrates the performance pitfall of creating inline functions for the
dataKeyprop, which causes unnecessary recalculation of all data points.
138-167: LGTM! Throttling implementation is correct.The custom hook properly implements throttling with a 50ms delay and trailing edge. The code structure is clean and reusable.
172-180: LGTM!The introduction strikes a good balance—reassuring users that performance is typically fine while acknowledging scenarios where optimization may be needed.
181-197: LGTM!The "Isolate components" section clearly explains the strategy and references React.memo appropriately for further optimization.
198-221: LGTM!The "Keep references stable" section provides excellent guidance on using useMemo and useCallback, with specific emphasis on the performance-critical
dataKeyprop.
252-271: LGTM!The "Keep it simple" section provides valuable perspective on data visualization best practices, encouraging thoughtful consideration of data volume and user comprehension. The references to aggregation techniques and D3 binning are helpful.
282-306: LGTM!The "Measure, measure, measure!" section provides excellent tool recommendations with direct links to official documentation. The prioritization of React DevTools followed by browser flame charts is practical and well-reasoned.
307-315: LGTM!The closing section appropriately encourages users to report performance issues after trying the suggested optimizations, acknowledging that improvements to Recharts itself may still be needed.
| const dataKeyAsFunctionGoodExample = ` | ||
| // Fast performant chart 🚀🚀🚀 | ||
| import { LineChart, Line, XAxis, YAxis } from 'recharts'; | ||
|
|
||
| const data = [ ...large dataset... ]; | ||
|
|
||
| /* | ||
| * dataKey function is defined outside the component, | ||
| * so that its reference is stable between renders. | ||
| */ | ||
| const dataKey = (entry) => entry.value1; | ||
|
|
||
| function GoodExample() { | ||
| /* | ||
| * Alternatively, you can use useCallback inside the component. | ||
| * useCallback keeps the same reference between renders, | ||
| * so that the Line does not need to recalculate | ||
| * points unless the function actually changes. | ||
| * useCallback is useful when the function depends on | ||
| * other props or state. If the dependencies change, | ||
| * a new function reference will be created. | ||
| * Which still re-renders the chart but hopefully less often. | ||
| */ | ||
| const dataKey = React.useCallback((entry) => entry.value1, [dependency]); | ||
| return ( | ||
| <LineChart data={data}> | ||
| <XAxis dataKey="name" /> | ||
| <YAxis /> | ||
| <Line dataKey={dataKey} /> | ||
| /* | ||
| * Another alternative: | ||
| * Simple dataKey functions can be declared as a string. | ||
| * Recharts will internally transform this into (entry) => entry.value1 | ||
| * function. String references are stable between renders, so this | ||
| * variant is likely to be more performant. | ||
| */ | ||
| <Line dataKey='value1' /> | ||
| </LineChart> | ||
| ); | ||
| } | ||
| `; |
There was a problem hiding this comment.
Fix undefined variable reference in example.
Line 119 references [dependency] in the useCallback dependency array, but dependency is not defined in the example. This could confuse users copying the code.
Consider one of these alternatives:
Option 1: Use empty array for stable reference
- const dataKey = React.useCallback((entry) => entry.value1, [dependency]);
+ const dataKey = React.useCallback((entry) => entry.value1, []);Option 2: Use a more concrete example
- const dataKey = React.useCallback((entry) => entry.value1, [dependency]);
+ const dataKey = React.useCallback((entry) => entry[someField], [someField]);Option 3: Add a comment to clarify
- const dataKey = React.useCallback((entry) => entry.value1, [dependency]);
+ const dataKey = React.useCallback((entry) => entry.value1, [/* add dependencies here */]);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const dataKeyAsFunctionGoodExample = ` | |
| // Fast performant chart 🚀🚀🚀 | |
| import { LineChart, Line, XAxis, YAxis } from 'recharts'; | |
| const data = [ ...large dataset... ]; | |
| /* | |
| * dataKey function is defined outside the component, | |
| * so that its reference is stable between renders. | |
| */ | |
| const dataKey = (entry) => entry.value1; | |
| function GoodExample() { | |
| /* | |
| * Alternatively, you can use useCallback inside the component. | |
| * useCallback keeps the same reference between renders, | |
| * so that the Line does not need to recalculate | |
| * points unless the function actually changes. | |
| * useCallback is useful when the function depends on | |
| * other props or state. If the dependencies change, | |
| * a new function reference will be created. | |
| * Which still re-renders the chart but hopefully less often. | |
| */ | |
| const dataKey = React.useCallback((entry) => entry.value1, [dependency]); | |
| return ( | |
| <LineChart data={data}> | |
| <XAxis dataKey="name" /> | |
| <YAxis /> | |
| <Line dataKey={dataKey} /> | |
| /* | |
| * Another alternative: | |
| * Simple dataKey functions can be declared as a string. | |
| * Recharts will internally transform this into (entry) => entry.value1 | |
| * function. String references are stable between renders, so this | |
| * variant is likely to be more performant. | |
| */ | |
| <Line dataKey='value1' /> | |
| </LineChart> | |
| ); | |
| } | |
| `; | |
| const dataKeyAsFunctionGoodExample = ` | |
| // Fast performant chart 🚀🚀🚀 | |
| import { LineChart, Line, XAxis, YAxis } from 'recharts'; | |
| const data = [ ...large dataset... ]; | |
| /* | |
| * dataKey function is defined outside the component, | |
| * so that its reference is stable between renders. | |
| */ | |
| const dataKey = (entry) => entry.value1; | |
| function GoodExample() { | |
| /* | |
| * Alternatively, you can use useCallback inside the component. | |
| * useCallback keeps the same reference between renders, | |
| * so that the Line does not need to recalculate | |
| * points unless the function actually changes. | |
| * useCallback is useful when the function depends on | |
| * other props or state. If the dependencies change, | |
| * a new function reference will be created. | |
| * Which still re-renders the chart but hopefully less often. | |
| */ | |
| const dataKey = React.useCallback((entry) => entry.value1, []); | |
| return ( | |
| <LineChart data={data}> | |
| <XAxis dataKey="name" /> | |
| <YAxis /> | |
| <Line dataKey={dataKey} /> | |
| /* | |
| * Another alternative: | |
| * Simple dataKey functions can be declared as a string. | |
| * Recharts will internally transform this into (entry) => entry.value1 | |
| * function. String references are stable between renders, so this | |
| * variant is likely to be more performant. | |
| */ | |
| <Line dataKey='value1' /> | |
| </LineChart> | |
| ); | |
| } | |
| `; |
🤖 Prompt for AI Agents
In www/src/components/GuideView/Performance/index.tsx around lines 96 to 136,
the example uses React.useCallback((entry) => entry.value1, [dependency]) but
`dependency` is undefined; update the example to remove the undefined variable
by either using an empty dependency array (useCallback(..., [])) for a stable
reference, or replace [dependency] with a concrete prop/state name if you want
to demonstrate changing dependencies, or add a short inline comment explaining
that `dependency` is a placeholder and should be replaced by real dependencies.
| <CodeMirrorEditor | ||
| value={` | ||
| { | ||
| "plugins": ["react-perf"], | ||
| "rules": { | ||
| /* | ||
| * New object as prop creates a new reference on each render. | ||
| * Recharts is optimized for this since 3.4.2. | ||
| * You may enable this rule if you are using an older version, | ||
| * or if you want to be extra cautious. | ||
| */ | ||
| // 'react-perf/jsx-no-new-object-as-prop': 'error', | ||
| 'react-perf/jsx-no-new-array-as-prop': 'error', | ||
| 'react-perf/jsx-no-new-function-as-prop': 'error', | ||
| 'react-perf/jsx-no-jsx-as-prop': 'error', | ||
| } | ||
| } | ||
| `} | ||
| /> |
There was a problem hiding this comment.
🧩 Analysis chain
Verify the version claim about object-as-prop optimization.
Line 239 claims "Recharts is optimized for this since 3.4.2." Please verify this version number is accurate, as users will rely on this information to decide whether to enable the ESLint rule.
🏁 Script executed:
#!/bin/bash
# Check Recharts CHANGELOG or release notes for version 3.4.2 object optimization
rg -n "3\.4\.2|object.*prop|new object" CHANGELOG.md --context 3Length of output: 0
I'll verify the Recharts 3.4.2 version claim by searching the repository structure and checking web sources for release information.
🏁 Script executed:
#!/bin/bash
# Find CHANGELOG, package.json, or release notes in the repository
fd -e CHANGELOG -e CHANGELOG.md -e CHANGELOG.txt -e RELEASES -e RELEASE_NOTES -e release-notes.md 2>/dev/null | head -20
echo "---"
# Search for any mention of recharts versions in the repo
rg -n "recharts|3\.4\.2" package.json package-lock.json yarn.lock --type json --type text 2>/dev/null | head -20Length of output: 3
🌐 Web query:
Recharts 3.4.2 release notes object prop optimization
💡 Result:
I couldn't find a v3.4.2 release for Recharts. The official GitHub Releases show v3.4.0 and v3.4.1 (Nov 10, 2025) and their changelogs — neither mentions an "object prop optimization". See Recharts releases/changelog on GitHub. [1]
If you meant a specific PR or issue about reducing re-renders from object props, tell me the PR/issue number or a link and I’ll look it up. [1]
Sources:
- Recharts Releases (v3.4.0 / v3.4.1 changelogs) — GitHub. [1]
Remove or correct the inaccurate Recharts version claim.
Recharts v3.4.2 does not exist; only v3.4.0 and v3.4.1 are released (as of Nov 10, 2025), and neither changelog mentions an "object prop optimization". The comment at line 239 claiming "Recharts is optimized for this since 3.4.2" is incorrect and will mislead users. Either remove the version claim, verify the actual version where this optimization was introduced, or provide a source supporting the claim.
🤖 Prompt for AI Agents
In www/src/components/GuideView/Performance/index.tsx around lines 232 to 250,
the inline comment claiming "Recharts is optimized for this since 3.4.2" is
incorrect (3.4.2 doesn't exist) and must be fixed; either remove the version
claim entirely, or replace it with the correct released version that introduced
the optimization and add a brief citation URL to the Recharts changelog or PR
that proves it, or if you cannot verify a version, reword the comment to omit
any specific version (e.g., "Recent Recharts versions handle this optimization")
so the guidance remains accurate.
There was a problem hiding this comment.
Yeah we're about to release 3.4.2 where the optimizations are included.
There was a problem hiding this comment.
@PavelVanecek, thanks for the clarification! Since 3.4.2 isn't released yet, you might want to consider one of these options:
- Add a note that 3.4.2 is an upcoming release, e.g., "Recharts will be optimized for this in the upcoming 3.4.2 release" or "Recent Recharts versions (3.4.2+) are optimized for this"
- Time the merge of this PR to coincide with the 3.4.2 release
- Keep it as-is if you plan to release 3.4.2 before merging this documentation
This will help avoid confusion for users who might read the docs before 3.4.2 is available.
✏️ Learnings added
Learnt from: PavelVanecek
Repo: recharts/recharts PR: 6659
File: www/src/components/GuideView/Performance/index.tsx:232-250
Timestamp: 2025-11-19T14:08:01.708Z
Learning: In Recharts version 3.4.2, object-as-prop optimizations were introduced to reduce unnecessary re-renders when new object references are passed as props. This affects the recommendation for the `react-perf/jsx-no-new-object-as-prop` ESLint rule.
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
🧠 Learnings used
Learnt from: PavelVanecek
Repo: recharts/recharts PR: 6640
File: src/cartesian/Bar.tsx:156-159
Timestamp: 2025-11-16T09:14:24.891Z
Learning: In recharts, SSR (Server-Side Rendering) is not yet supported—charts don't render at all in SSR environments. The `isAnimationActive: 'auto'` mode is preparatory work for future SSR support, so testing of this mode should be deferred until SSR support is actually implemented.
| <section> | ||
| <h2>Wait a little</h2> | ||
| <p> | ||
| Mouse events can fire very frequently, and if your chart updates on every mouse event, it can become sluggish. | ||
| One can go to very great lengths to optimize, but sometimes a simpler solution is to just wait a little, add | ||
| debounce and only redraw the chart after the user has stopped moving the mouse for a short period of time. | ||
| </p> | ||
| <h3>Example of debounced mouse handler</h3> | ||
| <CodeMirrorEditor value={throttledMouseHandlerExample} /> | ||
| </section> |
There was a problem hiding this comment.
Clarify throttle vs. debounce terminology.
The text uses "debounce" terminology (lines 276-277: "add debounce and only redraw the chart after the user has stopped moving the mouse"), but the code example implements throttle, not debounce. These are different patterns:
- Throttle: Fires at most once per time interval during continuous input (what the code implements)
- Debounce: Resets the timer on each event and only fires after input stops (what the text describes)
Consider revising the text to accurately describe throttling:
Mouse events can fire very frequently, and if your chart updates on every mouse event, it can become sluggish.
One can go to very great lengths to optimize, but sometimes a simpler solution is to just wait a little, add
- debounce and only redraw the chart after the user has stopped moving the mouse for a short period of time.
+ throttle to limit how often the chart redraws, ensuring updates happen at most once per time interval.
</p>
- <h3>Example of debounced mouse handler</h3>
+ <h3>Example of throttled mouse handler</h3>Alternatively, if you prefer to demonstrate debouncing instead, replace the throttle call with a debounce implementation.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <section> | |
| <h2>Wait a little</h2> | |
| <p> | |
| Mouse events can fire very frequently, and if your chart updates on every mouse event, it can become sluggish. | |
| One can go to very great lengths to optimize, but sometimes a simpler solution is to just wait a little, add | |
| debounce and only redraw the chart after the user has stopped moving the mouse for a short period of time. | |
| </p> | |
| <h3>Example of debounced mouse handler</h3> | |
| <CodeMirrorEditor value={throttledMouseHandlerExample} /> | |
| </section> | |
| <section> | |
| <h2>Wait a little</h2> | |
| <p> | |
| Mouse events can fire very frequently, and if your chart updates on every mouse event, it can become sluggish. | |
| One can go to very great lengths to optimize, but sometimes a simpler solution is to just wait a little, add | |
| throttle to limit how often the chart redraws, ensuring updates happen at most once per time interval. | |
| </p> | |
| <h3>Example of throttled mouse handler</h3> | |
| <CodeMirrorEditor value={throttledMouseHandlerExample} /> | |
| </section> |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #6659 +/- ##
==========================================
- Coverage 94.26% 94.04% -0.22%
==========================================
Files 496 497 +1
Lines 42442 42564 +122
Branches 4859 4859
==========================================
+ Hits 40009 40031 +22
- Misses 2428 2528 +100
Partials 5 5 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Bundle ReportBundle size has no change ✅ |
Description
I think this is a reasonable ask.
Related Issue
#6624
Motivation and Context
We need to have some sort of contract. "Try these things first before creating an issue".
Summary by CodeRabbit