-
-
Notifications
You must be signed in to change notification settings - Fork 32.8k
Description
Steps to reproduce
Steps:
- Open this link to live example: https://codesandbox.io/p/devbox/mui-timeline-nextjs-spxvgc
- Navigate to the
/serverroute — you’ll see extra space appearing even thoughTimelineOppositeContentis present.
This issue occurs when TimelineItem is rendered inside a Server Component (e.g. a component without 'use client' in a Next.js App Router environment).
Current behavior
The ::before styling is incorrectly applied to TimelineItem even when TimelineOppositeContent is present, causing unwanted extra space at the start of the row.
Expected behavior
The ::before styling should not be applied to TimelineItem when TimelineOppositeContent is present.
Context
It appears that when the TimelineItem component is rendered on the server (i.e., without 'use client'), props.children is not being passed. As a result, React.Children.toArray(props.children) will always return [], incorrectly indicating that TimelineOppositeContent is not present.
material-ui/packages/mui-lab/src/TimelineItem/TimelineItem.js
Lines 71 to 75 in 5052446
| React.Children.forEach(props.children, (child) => { | |
| if (isMuiElement(child, ['TimelineOppositeContent'])) { | |
| hasOppositeContent = true; | |
| } | |
| }); |
Since JSX children are not serializable, React does not pass them from server components to client components.
Proposed Solution
This can be addressed using pure CSS by leveraging the :has() selector to detect whether a <TimelineItem> contains a child with the MuiTimelineOppositeContent-root class (the default class applied to TimelineOppositeContent). Based on that, conditional styles can be applied.
I've been testing this approach locally and I can submit a PR shortly!
Your environment
System:
OS: macOS 14.5
Binaries:
Node: 22.16.0 - ~/.nvm/versions/node/v22.16.0/bin/node
npm: 10.9.2 - ~/.nvm/versions/node/v22.16.0/bin/npm
pnpm: 10.13.1 - ~/Library/pnpm/pnpm
Browsers:
Chrome: 138.0.7204.184
Edge: 138.0.3351.109
Safari: 17.5
npmPackages:
@mui/internal-babel-plugin-minify-errors: ^2.0.8-canary.3 => 2.0.8-canary.3
@mui/internal-babel-plugin-resolve-imports: ^2.0.7-canary.11 => 2.0.7-canary.11
@mui/internal-bundle-size-checker: ^1.0.9-canary.10 => 1.0.9-canary.10
@mui/internal-code-infra: 0.0.2-canary.22 => 0.0.2-canary.22
@mui/internal-docs-utils: workspace:^ => 2.0.1
@mui/internal-scripts: workspace:^ => 2.0.10
@mui/internal-test-utils: workspace:^ => 2.0.10
@mui/material: workspace:^ => 7.2.0
@mui/utils: workspace:^ => 7.2.0
@pigment-css/react: 0.0.30 => 0.0.30
@types/react: ^19.1.8 => 19.1.8
typescript: ^5.8.3 => 5.8.3
Search keywords: Timeline TimelineItem TimelineOppositeContent