From be081a5441f187fcf39b08e3cbc8df112f2e40fe Mon Sep 17 00:00:00 2001 From: Damian Pieczynski Date: Tue, 30 Dec 2025 08:34:20 +0100 Subject: [PATCH 1/3] feat(react-virtual): add useFlushSync option --- docs/framework/react/react-virtual.md | 45 +++++++++++++++++++++++++-- packages/react-virtual/package.json | 2 +- packages/react-virtual/src/index.tsx | 23 ++++++++++---- 3 files changed, 61 insertions(+), 9 deletions(-) diff --git a/docs/framework/react/react-virtual.md b/docs/framework/react/react-virtual.md index e32a982ee..338d32052 100644 --- a/docs/framework/react/react-virtual.md +++ b/docs/framework/react/react-virtual.md @@ -9,7 +9,7 @@ The `@tanstack/react-virtual` adapter is a wrapper around the core virtual logic ```tsx function useVirtualizer( options: PartialKeys< - VirtualizerOptions, + ReactVirtualizerOptions, 'observeElementRect' | 'observeElementOffset' | 'scrollToFn' >, ): Virtualizer @@ -22,7 +22,7 @@ This function returns a standard `Virtualizer` instance configured to work with ```tsx function useWindowVirtualizer( options: PartialKeys< - VirtualizerOptions, + ReactVirtualizerOptions, | 'getScrollElement' | 'observeElementRect' | 'observeElementOffset' @@ -32,3 +32,44 @@ function useWindowVirtualizer( ``` This function returns a window-based `Virtualizer` instance configured to work with the window as the scrollElement. + +## React-Specific Options + +### `useFlushSync` + +```tsx +type ReactVirtualizerOptions = + VirtualizerOptions & { + useFlushSync?: boolean + } +``` + +Both `useVirtualizer` and `useWindowVirtualizer` accept a `useFlushSync` option that controls whether React's `flushSync` is used for synchronous updates. + +- **Type**: `boolean` +- **Default**: `true` +- **Description**: When `true`, the virtualizer will use `flushSync` from `react-dom` to ensure synchronous rendering during scroll events. This provides the most accurate scrolling behavior but may impact performance in some scenarios. + +#### When to disable `useFlushSync` + +You may want to set `useFlushSync: false` in the following scenarios: + +- **React 19 compatibility**: In React 19, you may see the following console warning when scrolling: + ``` + flushSync was called from inside a lifecycle method. React cannot flush when React is already rendering. Consider moving this call to a scheduler task or micro task. + ``` + Setting `useFlushSync: false` will eliminate this warning by allowing React to batch updates naturally. +- **Performance optimization**: If you experience performance issues with rapid scrolling on lower-end devices +- **Testing environments**: When running tests that don't require synchronous DOM updates +- **Non-critical lists**: When slight visual delays during scrolling are acceptable for better overall performance + +#### Example + +```tsx +const virtualizer = useVirtualizer({ + count: 10000, + getScrollElement: () => parentRef.current, + estimateSize: () => 50, + useFlushSync: false, // Disable synchronous updates +}) +``` diff --git a/packages/react-virtual/package.json b/packages/react-virtual/package.json index c25600380..a5def4a5b 100644 --- a/packages/react-virtual/package.json +++ b/packages/react-virtual/package.json @@ -30,7 +30,7 @@ "test:lib:dev": "pnpm run test:lib --watch", "test:build": "publint --strict", "build": "vite build", - "test:e2e": "playwright test" + "test:e2e": "../../node_modules/.bin/playwright test" }, "type": "module", "types": "dist/esm/index.d.ts", diff --git a/packages/react-virtual/src/index.tsx b/packages/react-virtual/src/index.tsx index d835e7c4b..313c3d4f9 100644 --- a/packages/react-virtual/src/index.tsx +++ b/packages/react-virtual/src/index.tsx @@ -16,18 +16,29 @@ export * from '@tanstack/virtual-core' const useIsomorphicLayoutEffect = typeof document !== 'undefined' ? React.useLayoutEffect : React.useEffect +export type ReactVirtualizerOptions< + TScrollElement extends Element | Window, + TItemElement extends Element, +> = VirtualizerOptions & { + useFlushSync?: boolean +} + function useVirtualizerBase< TScrollElement extends Element | Window, TItemElement extends Element, ->( - options: VirtualizerOptions, -): Virtualizer { +>({ + useFlushSync = true, + ...options +}: ReactVirtualizerOptions): Virtualizer< + TScrollElement, + TItemElement +> { const rerender = React.useReducer(() => ({}), {})[1] const resolvedOptions: VirtualizerOptions = { ...options, onChange: (instance, sync) => { - if (sync) { + if (useFlushSync && sync) { flushSync(rerender) } else { rerender() @@ -58,7 +69,7 @@ export function useVirtualizer< TItemElement extends Element, >( options: PartialKeys< - VirtualizerOptions, + ReactVirtualizerOptions, 'observeElementRect' | 'observeElementOffset' | 'scrollToFn' >, ): Virtualizer { @@ -72,7 +83,7 @@ export function useVirtualizer< export function useWindowVirtualizer( options: PartialKeys< - VirtualizerOptions, + ReactVirtualizerOptions, | 'getScrollElement' | 'observeElementRect' | 'observeElementOffset' From 08c210e5b0d3a44277763ac39fe237140bf0ba26 Mon Sep 17 00:00:00 2001 From: Damian Pieczynski Date: Tue, 30 Dec 2025 08:55:25 +0100 Subject: [PATCH 2/3] chore: add changeset --- .changeset/angry-poets-visit.md | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 .changeset/angry-poets-visit.md diff --git a/.changeset/angry-poets-visit.md b/.changeset/angry-poets-visit.md new file mode 100644 index 000000000..2a89d24d8 --- /dev/null +++ b/.changeset/angry-poets-visit.md @@ -0,0 +1,10 @@ +--- +"@tanstack/react-virtual": patch +--- + +feat(react-virtual): add `useFlushSync` option + +Adds a React-specific `useFlushSync` option to control whether `flushSync` is used for synchronous scroll correction during measurement. + +The default behavior remains unchanged (`useFlushSync: true`) to preserve the best scrolling experience. +Disabling it avoids the React 19 warning about calling `flushSync` during render, at the cost of potentially increased visible whitespace during fast scrolling with dynamically sized items. From 0c8a49949d8d7481d2a38fefd955709fb927e7e0 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Tue, 30 Dec 2025 07:56:06 +0000 Subject: [PATCH 3/3] ci: apply automated fixes --- .changeset/angry-poets-visit.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.changeset/angry-poets-visit.md b/.changeset/angry-poets-visit.md index 2a89d24d8..4958cbfa6 100644 --- a/.changeset/angry-poets-visit.md +++ b/.changeset/angry-poets-visit.md @@ -1,10 +1,10 @@ --- -"@tanstack/react-virtual": patch +'@tanstack/react-virtual': patch --- feat(react-virtual): add `useFlushSync` option Adds a React-specific `useFlushSync` option to control whether `flushSync` is used for synchronous scroll correction during measurement. -The default behavior remains unchanged (`useFlushSync: true`) to preserve the best scrolling experience. +The default behavior remains unchanged (`useFlushSync: true`) to preserve the best scrolling experience. Disabling it avoids the React 19 warning about calling `flushSync` during render, at the cost of potentially increased visible whitespace during fast scrolling with dynamically sized items.