Skip to content

feat(react-virtual): add directDomUpdates for re-render-free scroll positioning#1180

Merged
tannerlinsley merged 2 commits into
TanStack:mainfrom
piecyk:damian/feat/react-direct-dom-updates
Jun 1, 2026
Merged

feat(react-virtual): add directDomUpdates for re-render-free scroll positioning#1180
tannerlinsley merged 2 commits into
TanStack:mainfrom
piecyk:damian/feat/react-direct-dom-updates

Conversation

@piecyk
Copy link
Copy Markdown
Collaborator

@piecyk piecyk commented Jun 1, 2026

Adds opt-in directDomUpdates to useVirtualizer / useWindowVirtualizer. When enabled, item positions (top/left or transform) and container size (height/width) are written directly to the DOM on every onChange, and React only re-renders on range or isScrolling changes.

  • New directDomUpdatesMode: 'position' | 'transform' to pick strategy
  • New virtualizer.containerRef for the inner sized element

🎯 Changes

✅ Checklist

  • I have followed the steps in the Contributing guide.
  • I have tested this code locally with pnpm run test:pr.

🚀 Release Impact

  • This change affects published code, and I have generated a changeset.
  • This change is docs/CI/dev-only (no release).

Summary by CodeRabbit

  • New Features

    • Opt-in direct DOM updates for the virtualized list with selectable positioning modes (position/transform) for smoother scrolling and reduced rerenders.
    • Exposed container hookup to enable immediate sizing and direct positioning behavior.
  • Tests

    • Added end-to-end tests covering direct DOM updates across positioning modes, scroll behaviors, and render-count assertions.
  • Chores

    • Release note entry added to mark the opt-in feature.

…ositioning

Adds opt-in `directDomUpdates` to `useVirtualizer` / `useWindowVirtualizer`.
When enabled, item positions (top/left or transform) and container size
(height/width) are written directly to the DOM on every onChange, and
React only re-renders on range or isScrolling changes.

- New `directDomUpdatesMode: 'position' | 'transform'` to pick strategy
- New `virtualizer.containerRef` for the inner sized element
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 1, 2026

Review Change Stack

Caution

Review failed

Pull request was closed or merged during review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 67b1378b-1e73-446c-a2eb-47c6674ca4cb

📥 Commits

Reviewing files that changed from the base of the PR and between c22de3d and 0a6f4b0.

📒 Files selected for processing (5)
  • .changeset/direct-dom-updates.md
  • examples/react/dynamic/src/main.tsx
  • packages/react-virtual/e2e/app/direct-dom-updates/main.tsx
  • packages/react-virtual/e2e/app/test/direct-dom-updates.spec.ts
  • packages/react-virtual/src/index.tsx
✅ Files skipped from review due to trivial changes (1)
  • .changeset/direct-dom-updates.md
🚧 Files skipped from review as they are similar to previous changes (3)
  • packages/react-virtual/e2e/app/test/direct-dom-updates.spec.ts
  • packages/react-virtual/e2e/app/direct-dom-updates/main.tsx
  • packages/react-virtual/src/index.tsx

📝 Walkthrough

Walkthrough

Adds opt-in directDomUpdates to apply virtualized container sizing and per-item positioning directly to the DOM, minimizing React re-renders. Exposes containerRef, updates hooks/types, updates an example, and adds an E2E app plus Playwright tests for "position" and "transform" modes.

Changes

Direct DOM Updates

Layer / File(s) Summary
Hook types and core directDomUpdates implementation
packages/react-virtual/src/index.tsx
Adds ReactVirtualizer and extends options with directDomUpdates, directDomUpdatesMode, and useFlushSync; implements direct DOM style writes in useVirtualizerBase, gates React re-renders to range/isScrolling changes, exposes containerRef, and reapplies styles after commits.
Example usage in dynamic virtualizer
examples/react/dynamic/src/main.tsx
Enables directDomUpdates and refactors RowVirtualizerDynamic to use virtualizer.containerRef, render virtualizer.getVirtualItems() as absolutely-positioned rows, attach virtualizer.measureElement, and add data-testid/data-index and parity classes.
E2E app and build configuration
packages/react-virtual/e2e/app/direct-dom-updates/index.html, packages/react-virtual/e2e/app/vite.config.ts, packages/react-virtual/e2e/app/direct-dom-updates/main.tsx
Adds a direct-dom-updates E2E app entrypoint and React main that initializes a 1000-item virtualizer with directDomUpdates: true, reads mode from the URL to set directDomUpdatesMode, and registers the app in Vite build inputs.
E2E test suite for directDomUpdates
packages/react-virtual/e2e/app/test/direct-dom-updates.spec.ts
Adds Playwright tests covering both position and transform modes: container height, initial item positioning, small-scroll non-rerender behavior, and large-scroll rerender verification.

🎯 3 (Moderate) | ⏱️ ~25 minutes

"🐰 I hopped through props and refs,
I nudged the DOM with careful steps,
No needless renders on the run,
Smooth scrolling, job well done,
Tests hop in — all checks are met!"

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive The description covers the core feature and API additions, but the required checklist items remain unchecked and the template sections are incomplete. Complete the checklist by checking off contribution guide compliance and test verification items, and mark the appropriate Release Impact checkbox.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main feature addition: direct DOM updates for scroll positioning without unnecessary React re-renders.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@nx-cloud
Copy link
Copy Markdown

nx-cloud Bot commented Jun 1, 2026

View your CI Pipeline Execution ↗ for commit 0a6f4b0

Command Status Duration Result
nx affected --targets=test:sherif,test:knip,tes... ✅ Succeeded 36s View ↗
nx run-many --target=build --exclude=examples/** ✅ Succeeded 17s View ↗

☁️ Nx Cloud last updated this comment at 2026-06-01 16:00:12 UTC

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented Jun 1, 2026

More templates

@tanstack/angular-virtual

npm i https://pkg.pr.new/@tanstack/angular-virtual@1180

@tanstack/lit-virtual

npm i https://pkg.pr.new/@tanstack/lit-virtual@1180

@tanstack/react-virtual

npm i https://pkg.pr.new/@tanstack/react-virtual@1180

@tanstack/solid-virtual

npm i https://pkg.pr.new/@tanstack/solid-virtual@1180

@tanstack/svelte-virtual

npm i https://pkg.pr.new/@tanstack/svelte-virtual@1180

@tanstack/virtual-core

npm i https://pkg.pr.new/@tanstack/virtual-core@1180

@tanstack/vue-virtual

npm i https://pkg.pr.new/@tanstack/vue-virtual@1180

commit: 0a6f4b0

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/react-virtual/e2e/app/direct-dom-updates/main.tsx`:
- Around line 11-13: The page currently sets mode via const params = new
URLSearchParams(window.location.search) and const mode = (params.get('mode') ??
'position') as 'position' | 'transform'; but the hook default is now
'transform', so change the fallback from 'position' to 'transform' (i.e., use
(params.get('mode') ?? 'transform')), ensuring the unparameterized route matches
the hook's directDomUpdatesMode default.

In `@packages/react-virtual/e2e/app/test/direct-dom-updates.spec.ts`:
- Around line 50-51: Replace the fixed sleep call page.waitForTimeout(50) with a
web-first Playwright assertion that waits for the DOM style on the locator
'[data-testid="item-1"]' to reflect the expected move; use
page.locator('[data-testid="item-1"]') and an
expect(...).toHaveAttribute('style', ...) that checks for the correct style
depending on the test's mode variable (match /top:\s*40px/ when mode ===
'position' else match /translate3d\(0px,\s*40px,\s*0px\)/) so Playwright
auto-retries instead of using a hard timeout.

In `@packages/react-virtual/src/index.tsx`:
- Around line 42-65: Update the JSDoc for the new direct DOM updates API to
match the implementation: change the default mode in the docs to 'transform'
(not 'position') and explicitly state that even in 'transform' mode items must
still be anchored with position: absolute and top: 0 / left: 0 (the virtualizer
then applies translate3d), and also clarify that 'position' mode writes
top/left; update the comment block around the directDomUpdates and
directDomUpdatesMode references so consumers of directDomUpdates and
directDomUpdatesMode configure items correctly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7ccd0d7a-edf7-4e4e-87b9-4f1b21f0b592

📥 Commits

Reviewing files that changed from the base of the PR and between 59b1f84 and c22de3d.

📒 Files selected for processing (6)
  • examples/react/dynamic/src/main.tsx
  • packages/react-virtual/e2e/app/direct-dom-updates/index.html
  • packages/react-virtual/e2e/app/direct-dom-updates/main.tsx
  • packages/react-virtual/e2e/app/test/direct-dom-updates.spec.ts
  • packages/react-virtual/e2e/app/vite.config.ts
  • packages/react-virtual/src/index.tsx

Comment thread packages/react-virtual/e2e/app/direct-dom-updates/main.tsx
Comment thread packages/react-virtual/e2e/app/test/direct-dom-updates.spec.ts Outdated
Comment thread packages/react-virtual/src/index.tsx
@tannerlinsley tannerlinsley merged commit 73e115d into TanStack:main Jun 1, 2026
9 of 10 checks passed
@github-actions github-actions Bot mentioned this pull request Jun 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants