Heavy CPU usage on virtualized tables?
Steps to reproduce:
1. Visit this codesandbox link (derived from https://tanstack.com/virtual/v3/docs/examples/react/fixed):
https://f2mml2-5173.csb.app/
2. In Chrome, open the Performance Monitor (Inspect Element -> Three dots icon at the top right -> "Open Drawer" -> in the opened drawer, show the "Performance Monitor" tab)
3. Scroll very fast in the third example (the table with cells) of the sandbox example using a mouse wheel. Notice the CPU spike. In the first example with plain rows, the CPU spike is very minimal.
I have attached a call tree to this post showing which function is taking up the most time in tanstack virtual, and it appears to be an
onChange
function. That function can be seen here:
https://github.com/TanStack/virtual/blob/62d6dc8cf917e8500b4709a59a46f936d9252f61/packages/react-virtual/src/index.tsx#L31
I have a couple questions:
1. Is this performance loss expected?
2. Is there any way to minimize the CPU usage here?React Virtual Fixed): Example | TanStack Virtual Docs
An example showing how to implement Fixed): in React Virtual
GitHub
virtual/packages/react-virtual/src/index.tsx at 62d6dc8cf917e8500b4...
🤖 Headless UI for Virtualizing Large Element Lists in JS/TS, React, Solid, Vue and Svelte - TanStack/virtual

11 Replies
evident-indigoOP•2y ago
Looking deeper into the issue, it appears to just be the flushSync usage on every scroll.
I think flushSync may not be the appropriate choice here, per the docs it should be used sparingly: https://react.dev/reference/react-dom/flushSync
flushSync usage was added to resolve this issue:
https://github.com/TanStack/virtual/issues/549
Seems like it fixes the scrolling, but with the caveat of high CPU usage. With data-heavy rows, the CPU usage is even more pronounced (upwards of 50%+ on scroll alone). Would like to help push for an alternative solution that addresses both issues
flushSync – React
The library for web and native user interfaces
GitHub
Running virtual examples with React 18 createRoot causes rendering ...
Describe the bug I have tested the examples/react/fixed example with React 18, so changed the init code to use the new createRoot / render API: ReactDOM.render( , document.getElementById('root&...
ambitious-aqua•2y ago
I'm developing (and testing) on a ~9 year old pc and I also have a bit of a lag in nearly every virtual scrolling implementation, if the row components are a bit heavier. Also I noticed that the final production build runs vastly better than in dev mode.
Not sure how, but react-virtuoso had a the better performance on my machine when scrolling really fast (feels like they skip some rendering calls when scrolling super-fast). My final choice was tanstack virtual because of the API and also the dynamic sizing of the scrolling container.
For performance optimizations I did memoization and added placeholders for very complex list entries. On modern machines (tested on a notebook) the virtual scrolling now feels like native, and on this machine it's a good to OK UX I'd say.
Also I'm using Chakra, which isn't too performance friendly for a lot of re-mounts and renderings, like you have it in a virtual scrolling use-case.
unwilling-turquoise•2y ago
Would like to help push for an alternative solution that addresses both issues@Boston would be great, do you have any ideas how we can tackle this? The performance is complex topic @NiklasPor as you noticed, it's not one thing but most cases many.
evident-indigoOP•2y ago
I am fairly new to using tanstack libs, been enjoying using them so far. Diving into the library internals for the first time here, so I don't have contextual info about what the goal of the flushSync call site is. Do we know what is the root cause of the scrolling lag issue that flushSync had solved? Because then we can start from there 👍
Doing a bit more research:
It looks like with React 18 and the introduction of concurrent rendering, the "flickering" bwamsellem had discovered in github issue #549 appears to be caused by React dropping renders at its own discretion. I would like to point out that in bwamsellem's demo (found here: https://react-ts-mfeisb.stackblitz.io/), scrolling appears smooth when using the mouse wheel, but when scrolling using the scrollbar, that is where the flickering issue arises.
The root cause of this is that scroll events are able to fire at a much faster rate than react renders. So much so in fact that React 18's new concurrent rendering is dropping renders in order to keep performance high.
In this case,
flushSync
solves the issue by forcing React to dump queued render updates and to forcefully rerender the virtual DOM tree.
A potential solution would be not to remove flushSync
altogether, but to reduce the rate at which it is called:
1. Limit its invocations to 60fps (in other words, throttle the scroll events triggering flushSync to be inline with the number of renders).
2. Only call flushSync
when the user is moving at a rapid rate with the scroll bar (using scrollTop
/scrollLeft
and the clientHeight
/clientWidth
, we can compute the delta of the user's scrolling, and if the user's scroll delta reaches a certain rate, such as the delta being greater than the clientHeight
/clientWidth
of the table, that is when we should flushSync
, because we know at that point the scrolling occurs faster than React's concurrent rendering can tolerate).
With these two changes, the CPU usage on mouse wheel scrolls should drop to previous levels, while high-frequency scrolls with the scrollbar should be able to keep up with rendering, while also reducing CPU usage
EDIT: It might be possible to remove flushSync altogether with just the throttling of scrolling-based rerenders limited to 60fpsconscious-sapphire•2y ago
I've been seeing the same issues. Any thoughts on this? @piecyk
unwilling-turquoise•2y ago
Great work @Boston basic we can wrap scroll event with rAF and see how it works, this can be tested without changes in core by providing observeElementOffset, like here
https://codesandbox.io/p/devbox/still-sound-76nsps?embed=1&file=%2Fsrc%2Fmain.tsx%3A36%2C1
CodeSandbox
CodeSandbox brings instant cloud development environments that keep you in flow.
unwilling-turquoise•2y ago
also @Jacob checkout the observeElementOffset from codesandbox
conscious-sapphire•2y ago
Am I going insane, or does that CodeSandbox not have any code files I can view? 😂

conscious-sapphire•2y ago
Weird, I used this link instead and it worked: https://codesandbox.io/p/devbox/still-sound-76nsps
unwilling-turquoise•2y ago
just as refernce
Yo yo, did someone had time to play around with throttle scroll events, any comments? Issue is that it's really hard to measure the performance here
evident-indigoOP•2y ago
I have forked the original codesandbox app (original found here: https://f2mml2-5173.csb.app/) and experimented with
observeElementOffset
on that same demo, but I have not noticed any noticeable improvements with observeElementOffset
applied to it, unfortunately. flushSync
is still causing the same performance impact. Note that I have only included the grid example on the fork, as the CPU usage spike is easier to profile with tables containing more DOM elements.
here is the fork I had made: https://codesandbox.io/p/devbox/blissful-wildflower-qlq4ht
Separate link: https://qlq4ht-5173.csb.app/
(Please let me know if any of these links do not work)