Skip to content

React 19 issue with useWindowVirtualizer #743

Closed

Description

@fat

Describe the bug

Sorry for the drive by issue… 

We ran into a problem with react 19 today that was a pita to track down. So dropping it here incase it's useful to anyone.

We had a simple useWindowVirutalizer in a client component in next.js w/ react 19 (like this):

const virtualizer = useWindowVirtualizer({
  count: branches.length,
  estimateSize: () => 66,
  overscan: 5,
  scrollMargin: listRef.current?.offsetTop ?? 0,
});

and virtualizerRef.getVirtualItems() was being hoisted / memo'd by the compiler in such a way that it was always returning 0 for getVirutalItems.

To fix it we just dropped the virtualizer in a ref…

	const virtualizer = useWindowVirtualizer({
		count: branches.length,
		estimateSize: () => 66,
		overscan: 5,
		scrollMargin: listRef.current?.offsetTop ?? 0,
	});

	// we do this because react 19 was optimzing it away
	const virtualizerRef = useRef(virtualizer);
	
	…
	
	virtualizerRef.current.getVirtualItems() // etc…

Your minimal, reproducible example

n/a

Steps to reproduce

Don't have time to spin up a test case sorry!

Expected behavior

Expected getVirtualItems() to return items.

How often does this bug happen?

Every time

Screenshots or Videos

No response

Platform

macos, chrome, react 19

tanstack-virtual version

3.7.0

TypeScript version

No response

Additional context

No response

Terms & Code of Conduct

  • I agree to follow this project's Code of Conduct
  • I understand that if my bug cannot be reliable reproduced in a debuggable environment, it will probably not be fixed and this issue may even be closed.

Activity

  1. Prains commented on Jul 3, 2024

    @Prains

    same issue here, but droppind a virtualizer in a ref does not helps

  2. izakfilmalter commented on Jul 3, 2024

    @izakfilmalter

    Running into the same issue. useRef seems to work for now for us.

  3. simonrey1 commented on Nov 14, 2024

    @simonrey1

    It gives me an issue with eslint however.

    Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)eslint(react-compiler/react-compiler)
    

    Edit: it might be removed in a PR
    https://github.com/facebook/react/pull/30843/files

  4. cacabo commented on Dec 11, 2024

    @cacabo

    +1, I am seeing this with:

    {
        "@tanstack/react-virtual": "^3.11.1",
        "react": "^18.3.1",
        "react-compiler-runtime": "^19.0.0-beta-63b359f-20241101",
        "react-dom": "^18.3.1",
    }

    Putting a hook in the component after .getVirtualItems appears to be another workaround

    Another option is "use no memo"

    https://react.dev/learn/react-compiler#something-is-not-working-after-compilation

  5. piecyk commented on Dec 12, 2024

    @piecyk
    Collaborator

    Yep, 'use no memo' seems like a reasonable temporary workaround! 👍

  6. zeorin commented on May 20, 2025

    @zeorin

    I work around this with the following hook:

    const useNoMemo = <const T>(factory: () => T): T => {
      'use no memo';
      return factory();
    }

    which I then use like this:

      const virtualItems = useNoMemo(() => virtualizer.getVirtualItems());

    This de-opts just the call to virtualizer.getVirtualItems without de-opting the rest of the component.

    See react/react#33057 (comment)

  7. kaushal-dhrangad-techno commented on May 28, 2026

    @kaushal-dhrangad-techno

    I work around this with the following hook:

    const useNoMemo = (factory: () => T): T => {
    'use no memo';
    return factory();
    }
    which I then use like this:

    const virtualItems = useNoMemo(() => virtualizer.getVirtualItems());
    This de-opts just the call to virtualizer.getVirtualItems without de-opting the rest of the component.

    See facebook/react#33057 (comment)

    +1 works for me :-) thanks!

  8. zeorin commented on May 30, 2026

    @zeorin

    I've published a compiler-compatible package that is a near drop-in replacement for @tanstack/react-virtual: react-hook-tanstack-virtual.

  9. piecyk commented on Jun 2, 2026

    @piecyk
    Collaborator

    This has been addressed with the directDomUpdates option. When enabled, the virtualizer writes item positions and container sizes directly to the DOM, bypassing React's render cycle for scroll-driven updates. This makes the React Compiler's memoization of getVirtualItems() harmless, since position updates no longer depend on React re-renders.

    const virtualizer = useWindowVirtualizer({
      count: items.length,
      estimateSize: () => 66,
      overscan: 5,
      directDomUpdates: true,
    });

    The feature is covered by dedicated e2e tests that build with babel-plugin-react-compiler and verify scrolling works correctly.

    Closing as resolved. If you're not using directDomUpdates, the "use no memo" workaround mentioned above remains a valid alternative.

  10. adi-works commented on Jul 19, 2026

    @adi-works

    @piecyk Slick, but it didn't work for me. However, the useNoMemo workaround did work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions