Skip to content

[docs] Add sticky headers recipe to RichTreeView customization - #22908

Merged
michelengelen merged 7 commits into
mui:masterfrom
michelengelen:recipe/treeview/18324-sticky-headers
Sep 4, 2026
Merged

michelengelen merged 7 commits into
mui:masterfrom
michelengelen:recipe/treeview/18324-sticky-headers

Conversation

@michelengelen

@michelengelen michelengelen commented Jun 22, 2026 •

Copy link
Copy Markdown
Member

Adds a new "Sticky headers" demo to the Tree View customization docs, showing how to pin expanded items to the top of a scrollable RichTreeView using position: sticky and the --TreeView-itemDepth CSS variable so deeper nested headers stack below shallower ones.

Fixes #18324

Signed-off-by: michel <jsnerdic@gmail.com>
@michelengelen michelengelen self-assigned this Jun 22, 2026
@michelengelen michelengelen added docs Improvements or additions to the documentation. scope: tree view Changes related to the tree view. This includes TreeView, TreeItem. recipe labels Jun 22, 2026
@code-infra-dashboard

code-infra-dashboard Bot commented Jun 22, 2026 •

Copy link
Copy Markdown

Deploy preview

Bundle size

Bundle Parsed size Gzip size
@mui/x-data-grid 0B(0.00%) 0B(0.00%)
@mui/x-data-grid-pro 0B(0.00%) 0B(0.00%)
@mui/x-data-grid-premium 0B(0.00%) 0B(0.00%)
@mui/x-charts 0B(0.00%) 0B(0.00%)
@mui/x-charts-pro 0B(0.00%) 0B(0.00%)
@mui/x-charts-premium 0B(0.00%) 0B(0.00%)
@mui/x-date-pickers 0B(0.00%) 0B(0.00%)
@mui/x-date-pickers-pro 0B(0.00%) 0B(0.00%)
@mui/x-tree-view 0B(0.00%) 0B(0.00%)
@mui/x-tree-view-pro 0B(0.00%) 0B(0.00%)
@mui/x-scheduler 0B(0.00%) 0B(0.00%)
@mui/x-scheduler-premium 0B(0.00%) 0B(0.00%)
@mui/x-chat 0B(0.00%) 0B(0.00%)
@mui/x-license 0B(0.00%) 0B(0.00%)

Details of bundle changes


Check out the code infra dashboard for more information about this PR.

@michelengelen michelengelen added the type: enhancement It’s an improvement, but we can’t make up our mind whether it's a bug fix or a new feature. label Jun 22, 2026
@zannager
zannager removed the request for review from mj12albert July 7, 2026 11:10

@LukasTy LukasTy left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Looks nice. 👍
WDYT about adding some extra padding at the bottom of the tree view to trigger stickiness for all parents?

Claude Opus 5 review

Verdict

Nice, well-scoped recipe and the mechanism does work -- verified on the deploy preview that depth-0 headers pin at 0px, depth-1 at 32px, z-index: calc(...) resolves to 100/99, and Collapse is overflow: visible once entered so nothing clips the sticky rows. Three things worth fixing before merge.

Findings

  • Expanded rows lose their hover / focus / selection styling. .css-hash .MuiTreeItem-content[data-expanded] is specificity (0,3,0), which beats TreeItem's own :hover, [data-focused] and [data-selected] (0,2,0), and also wins the tie against [data-selected]:hover and [data-selected][data-focused] on source order. Confirmed on the preview: an expanded row stays rgb(255,255,255) when data-focused / data-selected are set, while a leaf row correctly shows rgba(0,0,0,0.12) / rgba(25,118,210,0.08). Because TreeItemRoot sets outline: 0, that leaves every expanded item with no visible keyboard focus indicator at all. The opaque background is genuinely needed, so layer the state colors on top of it instead of dropping them -- I tested this variant on the preview and it keeps the opaque base while restoring the state colors:

    backgroundColor: (theme.vars || theme).palette.background.paper,
    backgroundImage:
      'linear-gradient(var(--sticky-overlay, transparent), var(--sticky-overlay, transparent))',
    '&:hover': { '--sticky-overlay': (theme.vars || theme).palette.action.hover },
    '&[data-focused]': { '--sticky-overlay': (theme.vars || theme).palette.action.focus },
    // and the same for '&[data-selected]'
    
  • The recipe only works with the nested DOM structure. With flat DOM (RichTreeViewPro default, domStructure="flat", or virtualization) every li is a direct child of the ul and is exactly one row tall, so the sticky content can never leave its containing block and the CSS silently does nothing. The two demos immediately above this one on the same page already carry that flat-vs-nested caveat; this section should say the same.

  • ITEM_HEIGHT = 32 is an unenforced assumption and its comment describes the wrong thing. The comment reads "One level of indentation, must match the depth used in top below", but the constant is the row height, not the indentation. Nothing pins the row to 32px -- it just happens to be body1's 24px line-height plus 8px of padding, so any typography or density change silently breaks the stacking offsets. RichTreeView accepts itemHeight, which sets --TreeView-itemHeight and forces the content height, so passing itemHeight={ITEM_HEIGHT} makes the recipe self-consistent.

  • Nit: theme.vars ?? theme -- every other docs demo uses theme.vars || theme (33 occurrences, 0 with ??).

@noraleonte noraleonte left a comment

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.

LGTM after a few nits 👌 Great job ✈️

// Extra space at the bottom so the last parents can also reach the top
// of the scroll container and stick.
paddingBottom: 224,
// Expanded items stick to the top of the scroll container while any of

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.

I think we can shorten all of these code comments and only leave a one liner comm if it is truly necessary. i.e. on the top or zIndex calculation

Otherwise they seem to 'pollute' the demo and also a bit redundant since most of these are stated in the .md above

import { RichTreeView } from '@mui/x-tree-view/RichTreeView';
import { treeItemClasses } from '@mui/x-tree-view/TreeItem';

const MUI_X_PRODUCTS = [

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.

can't we separate this to a new file? (there might already be a demo data file like this)

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.

yes, extraction to a file is an established docs pattern, but there is no existing file to reuse

We would need to create a new variable to test this. The reason why: in order to properly show the sticky header demo we need a data tree that has at least 3 levels.

If you want I can add a new variable and align the other demos with this as well (since most of them use inline data anyways) in a follow-up to this.

WDYT?

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.

follow-up is fine 👌

… files

Signed-off-by: michel <jsnerdic@gmail.com>
@michelengelen
michelengelen enabled auto-merge (squash) September 4, 2026 07:48
@michelengelen
michelengelen merged commit a6d8503 into mui:master Sep 4, 2026
22 checks passed
@michelengelen
michelengelen deleted the recipe/treeview/18324-sticky-headers branch September 7, 2026 10:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs Improvements or additions to the documentation. recipe scope: tree view Changes related to the tree view. This includes TreeView, TreeItem. type: enhancement It’s an improvement, but we can’t make up our mind whether it's a bug fix or a new feature.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[tree view] Add Sticky Headers to Rich Tree View

3 participants