Skip to content

Conversation

@atomiks
Copy link
Contributor

@atomiks atomiks commented Jan 20, 2026

Closes #3755

Before hydration, this renders the <NavigationMenu.Content> into the DOM with a hidden attribute. After hydration, it's removed from the DOM (and only then portaled into the popup element as normal)

@atomiks atomiks added the component: navigation menu Changes related to the navigation menu component. label Jan 20, 2026
@pkg-pr-new
Copy link

pkg-pr-new bot commented Jan 20, 2026

  • vite-css-base-ui-example

    pnpm add https://pkg.pr.new/mui/base-ui/@base-ui/react@3794
    
    pnpm add https://pkg.pr.new/mui/base-ui/@base-ui/utils@3794
    

commit: f87bf7e

@mui-bot
Copy link

mui-bot commented Jan 20, 2026

Bundle size report

Bundle Parsed size Gzip size
@base-ui/react 🔺+74B(+0.02%) 🔺+37B(+0.03%)

Details of bundle changes


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

@netlify
Copy link

netlify bot commented Jan 20, 2026

Deploy Preview for base-ui ready!

Name Link
🔨 Latest commit f87bf7e
🔍 Latest deploy log https://app.netlify.com/projects/base-ui/deploys/696ed6f5e9d6ab0008e3c0e0
😎 Deploy Preview https://deploy-preview-3794--base-ui.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@atomiks atomiks force-pushed the fix/navigation-menu-ssr branch from eb4920e to f87bf7e Compare January 20, 2026 01:14
@atomiks atomiks marked this pull request as ready for review January 20, 2026 01:17
@greptile-apps
Copy link

greptile-apps bot commented Jan 20, 2026

Greptile Summary

This PR adds server-side rendering support to NavigationMenu.Content by rendering content with a hidden attribute during SSR, then removing it from the DOM after hydration. The implementation uses a selfMounted state flag (set via useOnMount) to differentiate between SSR/initial render and post-hydration renders. During SSR and initial client render, content renders in-place with hidden=true to ensure SEO-friendly HTML. After hydration, the content either returns null (when closed) or portals into the viewport (when open).

  • Added useOnMount hook to track when component has hydrated
  • Modified rendering logic to show hidden content before hydration
  • Updated shouldRender logic to include !selfMounted condition
  • Props array now includes { hidden: true } when !selfMounted
  • Added comprehensive SSR tests validating hidden attribute and post-hydration behavior

The implementation correctly avoids hydration mismatches by ensuring SSR and initial client render produce identical output.

Confidence Score: 4/5

  • This PR is safe to merge with minor concerns about hydration warnings
  • The implementation is well-designed and uses a clever pattern to avoid hydration mismatches by matching SSR and client initial renders. Tests cover both SSR and post-hydration scenarios. Score is 4 rather than 5 due to potential hydration edge cases that should be tested in a real browser environment
  • No files require special attention - implementation follows established patterns from similar components like TabsIndicator

Important Files Changed

Filename Overview
packages/react/src/navigation-menu/content/NavigationMenuContent.tsx Adds SSR support by rendering content with hidden attribute before hydration, then portaling after mount
packages/react/src/navigation-menu/content/NavigationMenuContent.test.tsx Adds SSR tests validating content renders with hidden attribute on server and is removed post-hydration

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

2 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

Comment on lines +148 to +150
if (!selfMounted) {
return content;
}
Copy link

Choose a reason for hiding this comment

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

logic: hydration mismatch will occur between SSR (renders content in-place with hidden) and client (returns null or portal). consider adding suppressHydrationWarning to parent to avoid React warnings.

Suggested change
if (!selfMounted) {
return content;
}
if (!selfMounted) {
return <div suppressHydrationWarning>{content}</div>;
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/react/src/navigation-menu/content/NavigationMenuContent.tsx
Line: 148:150

Comment:
**logic:** hydration mismatch will occur between SSR (renders content in-place with `hidden`) and client (returns null or portal). consider adding `suppressHydrationWarning` to parent to avoid React warnings.

```suggestion
  if (!selfMounted) {
    return <div suppressHydrationWarning>{content}</div>;
  }
```

How can I resolve this? If you propose a fix, please make it concise.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

component: navigation menu Changes related to the navigation menu component.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[navigation menu] support for SSR / forceMount

2 participants