Skip to content

Table keyboard navigation: scrollIntoViewport containingElement centering causes vertical viewport jumps in bounded scroll containers #10196

Description

@les-friesen

Provide a general summary of the issue here

When keyboard-navigating a React Aria Table inside a bounded scroll container (explicit height/maxHeight) with sticky header/column and scroll-padding on the scroll container, scrollIntoViewport can cause sudden vertical viewport jumps even though focus remains on the correct cell.

This is especially visible when:

  • Moving horizontally along a row (ArrowLeft/ArrowRight) — scrollTop changes unexpectedly
  • Moving vertically at the edges (e.g. ArrowUp from the first body row to the column header, ArrowDown near the bottom) — viewport recenters to the middle of the table instead of scrolling one row

Stock RAC Storybook stories without scroll-padding + sticky + bounded vertical scroll do not reproduce the issue.

Screen.Recording.2026-06-12.at.5.03.20.PM.mov

🤔 Expected Behavior?

Keyboard navigation should scroll the bounded container just enough to keep the focused cell visible (respecting scroll-padding), without recentering the entire <table> in the viewport.

😯 Current Behavior

useSelectableCollection calls scrollIntoView(scrollContainer, element) (padding-aware), then scrollIntoViewport(element, { containingElement: table }).

In scrollIntoViewport (@react-aria/utils / react-aria scrollIntoView.ts), after targetElement.scrollIntoView({ block: 'nearest' }), if the cell's bounding rect moved by >1px in either axis, the fallback runs:

containingElement?.scrollIntoView?.({ block: 'center', inline: 'center' });
targetElement.scrollIntoView?.({ block: 'nearest' });

Horizontal keyboard moves change left, which trips this fallback and calls table.scrollIntoView({ block: 'center', ... }), causing unwanted vertical scroll. Vertical edge navigation has the same problem when top changes.

The padding-aware scrollIntoView on the scroll container already positions the cell correctly when scroll-padding accounts for sticky regions.

Repro in StackBlitz: https://stackblitz.com/edit/github-budc6mta-fdxhpe8x?file=src%2Findex.css

💁 Possible Solution

scrollIntoViewport runs a padding-aware scroll on scroll parents, then a native targetElement.scrollIntoView({ block: 'nearest' }), then — if position changed by >1px in either axis — calls containingElement.scrollIntoView({block: 'center', inline: 'center' }). That last step recenters the entire table and causes vertical viewport jumps during horizontal keyboard navigation.

Possible fix:

  1. Skip containingElement centering when the padding-aware scroll parent pass
    already satisfied visibility (especially with scroll-padding for sticky UI).
  2. If a containing-element fallback is still needed, only trigger it when
    vertical position remains unsatisfactory (originalTop vs newTop), not
    when only left changed.

Optional: expose centerContainingElement?: boolean (default false) for consumers who need the legacy behavior.

Related: #8977 (same fallback mechanism, different scroll context).

🔦 Context

We hit this building a Table with sticky header/column and scroll-padding on ResizableTableContainer to keep keyboard focus clear of pinned regions. We worked around it with a yarn patch on react-aria@3.48.0 that removes the containing-element centering fallback.

RAC version: 1.17.0 / react-aria 3.48.0

🖥️ Steps to Reproduce

Render a Table inside ResizableTableContainer with bounded height (e.g. maxHeight: 400px) and overflow: auto
Enable sticky header and/or sticky column
Set scroll-padding-block-start / scroll-padding-inline-start on the scroll container for sticky clearance
Keyboard-focus cells and move horizontally along a row, or ArrowUp/ArrowDown at the top/bottom of the visible area
Observe scrollTop jumping / viewport recentering while focus stays correct

Version

react-aria-components@1.17.0 (react-aria@3.48.0)

What browsers are you seeing the problem on?

Chrome, Safari, Firefox, Microsoft Edge

If other, please specify.

No response

What operating system are you using?

masOS

🧢 Your Company/Team

MaintainX

🕷 Tracking Issue

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions