diff --git a/src/material/sidenav/drawer.scss b/src/material/sidenav/drawer.scss
index f7e89895a873..8f2fbc08beea 100644
--- a/src/material/sidenav/drawer.scss
+++ b/src/material/sidenav/drawer.scss
@@ -109,6 +109,10 @@ $drawer-over-drawer-z-index: 4;
height: 100%;
overflow: auto;
+ &.mat-drawer-content-hidden {
+ opacity: 0;
+ }
+
.mat-drawer-transition & {
transition: {
duration: variables.$swift-ease-out-duration;
diff --git a/src/material/sidenav/drawer.ts b/src/material/sidenav/drawer.ts
index b05af6537231..361440a53834 100644
--- a/src/material/sidenav/drawer.ts
+++ b/src/material/sidenav/drawer.ts
@@ -93,6 +93,7 @@ export function MAT_DRAWER_DEFAULT_AUTOSIZE_FACTORY(): boolean {
'class': 'mat-drawer-content',
'[style.margin-left.px]': '_container._contentMargins.left',
'[style.margin-right.px]': '_container._contentMargins.right',
+ '[class.mat-drawer-content-hidden]': '_shouldBeHidden()',
},
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
@@ -104,6 +105,7 @@ export function MAT_DRAWER_DEFAULT_AUTOSIZE_FACTORY(): boolean {
],
})
export class MatDrawerContent extends CdkScrollable implements AfterContentInit {
+ private _platform = inject(Platform);
private _changeDetectorRef = inject(ChangeDetectorRef);
_container = inject(MatDrawerContainer);
@@ -122,6 +124,24 @@ export class MatDrawerContent extends CdkScrollable implements AfterContentInit
this._changeDetectorRef.markForCheck();
});
}
+
+ /** Determines whether the content element should be hidden from the user. */
+ protected _shouldBeHidden(): boolean {
+ // In some modes the content is pushed based on the width of the opened sidenavs, however on
+ // the server we can't measure the sidenav so the margin is always zero. This can cause the
+ // content to jump around when it's rendered on the server and hydrated on the client. We
+ // avoid it by hiding the content on the initial render and then showing it once the sidenav
+ // has been measured on the client.
+ if (this._platform.isBrowser) {
+ return false;
+ }
+
+ const {start, end} = this._container;
+ return (
+ (start != null && start.mode !== 'over' && start.opened) ||
+ (end != null && end.mode !== 'over' && end.opened)
+ );
+ }
}
/**
diff --git a/src/material/sidenav/sidenav.ts b/src/material/sidenav/sidenav.ts
index e7214532f921..3a877964d9c2 100644
--- a/src/material/sidenav/sidenav.ts
+++ b/src/material/sidenav/sidenav.ts
@@ -30,8 +30,6 @@ import {CdkScrollable} from '@angular/cdk/scrolling';
template: '',
host: {
'class': 'mat-drawer-content mat-sidenav-content',
- '[style.margin-left.px]': '_container._contentMargins.left',
- '[style.margin-right.px]': '_container._contentMargins.right',
},
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
diff --git a/tools/public_api_guard/material/sidenav.md b/tools/public_api_guard/material/sidenav.md
index 062c2ddddf52..8878fd1f9684 100644
--- a/tools/public_api_guard/material/sidenav.md
+++ b/tools/public_api_guard/material/sidenav.md
@@ -136,6 +136,7 @@ export class MatDrawerContent extends CdkScrollable implements AfterContentInit
_container: MatDrawerContainer;
// (undocumented)
ngAfterContentInit(): void;
+ protected _shouldBeHidden(): boolean;
// (undocumented)
static ɵcmp: i0.ɵɵComponentDeclaration;
// (undocumented)