Skip to content

[EuiTable/EuiBasicTable/EuiInMemoryTable] Add always-visible sticky scrollbar#9674

Merged
tkajtoch merged 16 commits into
elastic:mainfrom
tkajtoch:feat/tables-sticky-scrollbar
May 22, 2026
Merged

[EuiTable/EuiBasicTable/EuiInMemoryTable] Add always-visible sticky scrollbar#9674
tkajtoch merged 16 commits into
elastic:mainfrom
tkajtoch:feat/tables-sticky-scrollbar

Conversation

@tkajtoch
Copy link
Copy Markdown
Member

@tkajtoch tkajtoch commented May 21, 2026

Summary

Resolves #9620

This PR adds an opt-in, always-visible sticky horizontal scrollbar to EuiTable, EuiBasicTable, and EuiInMemoryTable to help users without trackpads scroll table contents horizontally.

Without this, scrolling the table horizontally requires one of the following:

  1. Holding shift when scrolling using the scroll wheel
  2. Using the middle mouse button (holding the scroll wheel button) on Windows
  3. Scrolling to the bottom of the table to grab the native scrollbar track

This feature is disabled by default and can be enabled by setting the stickyScrollbar prop to true. When disabled, it doesn't register any event listeners or observers and doesn't modify the DOM.

Note: The styles applied to the virtual scrollbar are made to resemble the native scrollbar styles as closely as possible, however, every platform renders scrollbars differently. We standardize scrollbar background and track colors in our global styles. It is expected that the virtual scrollbar will not look 100% the same on Windows or Linux - Windows adds left and right buttons, and Linux differs by distro, GUI library, and more.

API Changes

component / parent prop / child change description
EuiTable, EuiBasicTable, EuiInMemoryTable stickyScrollbar Added Enable the sticky scrollbar behavior. When enabled, a virtual scrollbar sticking to the bottom of the viewport will be rendered on top of the table when the total table height is larger than the viewport height.

Screenshots

Before After
sticky.scrollbar.before.mov
sticky.scrollbar.after.mov

Safari:

safari.mov

Chromium on Ubuntu + Gnome:

ubuntu.gnome.chromium.mov

Chromium on Debian + KDE:

debian.kde.chromium.mov

Firefox on Debian + KDE:

debian.kde.firefox.mov

Chromium on Debian + XFCE:

debian.xfce.chromium.mov

Firefox on Debian + XFCE:

debian.xfce.firefox.mov

Performance

Before/Disabled After/Enabled
stickyScrollbar performance - disabled stickyScrollbar performance - enabled

Measurements done by manually resizing the viewport - not suitable for 1:1 comparison.
When stickyScrollbar is enabled, rendering and painting time grows by ~60ms, and scripting by ~25ms. This is fully expected and considering that the virtual scrollbar element style refreshes on every resize and scroll event, the resource cost is actually pretty minimal. Using ResizeObserver and IntersectionObserver synchronized with requestAnimationFrame results in grade A performance. I consider these numbers good and see no objections shipping it as-is.

Impact Assessment

Note: Most PRs should be tested in Kibana to help gauge their Impact before merging.

  • 🔴 Breaking changes — What will break? How many usages in Kibana/Cloud UI are impacted?
  • [ ]~ 💅 Visual changes — May impact style overrides; could require visual testing. Explain and estimate impact.~
  • 🧪 Test impact — May break functional or snapshot tests (e.g., HTML structure, class names, default values).
  • 🔧 Hard to integrate — If changes require substantial updates to Kibana, please stage the changes and link them here.

Impact level: 🟢 Low to None

Release Readiness

  • Documentation: {link to docs page(s)}
  • Figma: {link to Figma or issue}
  • Migration guide: {steps or link, for breaking/visual changes or deprecations}
  • Adoption plan (new features): (see parent epic)

QA instructions for reviewer

  • Go to URL and verify the sticky scrollbar behavior
    • Confirm that the virtual scrollbar doesn't appear until the table overflows and stickyScrollbar is enabled
    • Confirm that the virtual scrollbar disappears as soon as the table overflow ends - resize browser window in and out
    • Confirm that the virtual scrollbar is draggable and simulates the native scrollbar behavior
    • Confirm that the virtual scrollbar works in supported browsers

Checklist before marking Ready for Review

Reviewer checklist

  • Approved Impact Assessment — Acceptable to merge given the consumer impact.
  • Approved Release Readiness — Docs, Figma, and migration info are sufficient to ship.

@tkajtoch tkajtoch self-assigned this May 21, 2026
@tkajtoch tkajtoch changed the title Feat/tables sticky scrollbar [EuiTable/EuiBasicTable/EuiInMemoryTable] Add sticky scrollbar May 21, 2026
@tkajtoch tkajtoch changed the title [EuiTable/EuiBasicTable/EuiInMemoryTable] Add sticky scrollbar [EuiTable/EuiBasicTable/EuiInMemoryTable] Add always-visible sticky scrollbar May 22, 2026
Comment thread packages/eui/src/components/basic_table/basic_table.tsx Outdated
</EuiTableIsResponsiveContext.Provider>
</table>
</div>
<>
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This should not result in snapshot changes unless stickyScrollbar is explicitly enabled for a table, which is what we want.

@acstll acstll self-requested a review May 22, 2026 10:02
fireEvent.pointerMove(track, {
clientX: 150,
});
const pointerDownEvent = new MouseEvent('pointerdown', {
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This is needed for React 17 test runs. I explored replacing this logic with @testing-library/user-event but it would require upgrading that library.

@tkajtoch tkajtoch marked this pull request as ready for review May 22, 2026 13:12
@tkajtoch tkajtoch requested a review from a team as a code owner May 22, 2026 13:12
@@ -0,0 +1 @@
- Added experimental support for always-visible sticky horizontal scrollbars in `EuiTable`, `EuiBasicTable` and `EuiInMemoryTable` useful for dense tables that exceed the height of the viewport. This feature is currently opt-in and can be enabled by setting `stickyScrollbar: true`.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

If it's experimental, should we mark it as beta, add it to the Beta schedule and add a note in the docs?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

It's marked as beta in JSDoc, and yeah, I'm going to add it to our Beta schedule!

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Docs update: 7756dd0

Copy link
Copy Markdown
Contributor

@acstll acstll left a comment

Choose a reason for hiding this comment

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

🟢 I tried breaking it but couldn't, works really well. (Did not test on a touch screen device, but there scrolling happens mostly via gestures, no? 🤔). Left a single non-blocking comment for your consideration.

Comment on lines +13 to +15
* This mock doesn't provide a real IntersectionObserver implementation
* and should only be used for simple assertions.
* Use Cypress to test more complex scenarios end to end.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

thanks for adding the comment 💚

Comment thread packages/eui/src/components/table/sticky_scrollbar/sticky_scrollbar.tsx Outdated
Comment thread packages/eui/src/components/table/sticky_scrollbar/sticky_scrollbar.test.tsx Outdated
@elasticmachine
Copy link
Copy Markdown
Collaborator

💚 Build Succeeded

History

cc @tkajtoch

@elasticmachine
Copy link
Copy Markdown
Collaborator

💚 Build Succeeded

History

cc @tkajtoch

@tkajtoch tkajtoch merged commit ba2c877 into elastic:main May 22, 2026
5 checks passed
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.

[EuiBasicTable/EuiInMemoryTable] Implement a solution to always visible horizontal scrollbars

4 participants