Skip to content

[TimelineItem] Extra space due to ::before even when TimelineOppositeContent is present #46639

@tyalau

Description

@tyalau

Steps to reproduce

Steps:

  1. Open this link to live example: https://codesandbox.io/p/devbox/mui-timeline-nextjs-spxvgc
  2. Navigate to the /server route — you’ll see extra space appearing even though TimelineOppositeContent is present.
Image Image

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.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    breaking changecomponent: timelineThis is the name of the generic UI component, not the React module!enhancementThis is not a bug, nor a new featurev8.x

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions