Hey everyone! My team is dealing with a sneaky memory leak in our app, where we're using React and D3 to render these massive SVG graphs (like thousands of nodes and edges). Each time we zoom, pan, or filter, we replace the old SVG with a new one. We're super diligent about cleanup—using `useEffect` to remove elements with `d3.select().remove()`, aborting fetches, clearing timers, and removing event listeners when unmounting. But we're noticing something strange: after about an hour of heavy usage, Chrome DevTools shows a slow increase in memory consumption (DOM nodes, listeners, heap). It's gradual, not a big spike, but the app starts to lag. We've ruled out typical issues like globals and dangling timers.
Our best guess is something in the deeper layers of DOM/SVG or a quirk in the engine is holding onto refs even after we remove nodes. We're trying heap snapshots to track it down, but the leak is tough to catch due to its slow creep. Has anyone faced this before, particularly with React and D3 managing large, dynamic SVGs? Any hidden pitfalls you've encountered that could lead to these kinds of memory leaks? I'd love any tips or insights you have! Thanks in advance!
7 Answers
These bugs are notoriously challenging! One thing to watch for might be dangling promises. Double-check that all your promises resolve properly, or you could be inadvertently holding onto something in memory that prevents cleanup. Good luck!
To replicate the issue:
1. Write a small script that zooms/pans rapidly to see if that triggers the leak more quickly.
2. Test it across different browsers like Chrome, Firefox, and Safari to determine if it’s browser-specific. If it only happens in one, it might be a browser issue; if all show the leak, it’s likely in your code.
3. Once you can reproduce it, start removing parts of your code until the leak disappears to narrow down the culprit.
We’re already running automated tests to zoom, pan, and rebuild SVG multiple times and the leak shows up a lot quicker this way. Same slow creep on both Chrome and Firefox, but we’re testing Safari next. If it’s in our code or libraries, it’s tricky. We’re currently chopping features to see if anything changes. Thanks for the detailed strategy!
Keep in mind that memory management in browsers can be complex. Even when an object is freed, the memory might not be released immediately due to fragmentation or how the garbage collector operates. You might want to measure memory over time after one render and optimize from there. You could even create your own object allocator if you consistently need to reuse certain objects. Just a thought!
It could be a simple thing, but if you're logging SVG elements for debugging, those console logs might be keeping references to the elements and preventing them from being garbage collected. Just double-check that you're not logging them unnecessarily!
Hey, that’s a solid tip! We do have some debug logs for state checks. I’ll go through and remove those just to be sure, since even a stray console.log can mess with garbage collection. Appreciate your help!
This happens often with React + D3 + large SVGs. Ensure you're cleaning up:
- D3 transitions
- Hidden event listeners (like on window or doc)
- SVG definitions/gradients that may linger
- React refs/closures that keep old nodes alive.
For large graphs, consider using canvas/WebGL instead for better performance. It might save you from these memory issues.
Thanks for the input! We’ve been cleaning up D3 transitions, refs, and listeners, but detached nodes still build up. We’re checking for lingering gradients and event listeners too—those can get missed sometimes. The canvas/WebGL suggestion is noted. We're focused on fixing this now!
Before you go deeper, can you comment out the SVG creation part and take a heap snapshot just destroying the old one? That might shed some light on what’s causing the memory usage!
Why do you need to recreate the SVG every time you zoom or pan? Is it to only render what’s currently visible? Maybe there’s a more efficient way to handle rendering without tearing down the SVG each time.

We already triple-checked all promises, and they clean up as expected, but the memory creep persists. It’s frustrating! I think it might be something deeper or even a browser quirk at this point.