The PageLayout component provides a structured CSS Grid layout with four areas: Header, Navigation, Body, and Footer. It is used to create consistent page structures across your portal.
The layout uses a CSS Grid with the following areas:
Header — Spans the full width at the top. Typically contains a MenuBar with save/refresh buttons or page titles.Navigation — Left column below the header. Used for side navigation menus. Scrolls independently from the body.Body — Main content area. Scrolls independently and receives padding automatically.Footer — Spans the full width at the bottom. Typically contains copyright or status information.// React's PageLayout takes slot content via props (header/navigation/
// body/footer) instead of nested elements — gives TypeScript a place to
// enforce slot constraints. Equivalent to the Razor markup on the left.
<PageLayout
header={
<>
<SaveContextButton />
<RefreshContextButton />
</>
}
navigation={/* side navigation content */}
body={/* main page content */}
footer={<span>© My Company 2026</span>}
/><PageLayout>
<Header>
<MenuBar>
<SaveContextButton />
<RefreshContextButton />
</MenuBar>
</Header>
<Navigation>
<!-- Side navigation content -->
</Navigation>
<Body>
<!-- Main page content -->
</Body>
<Footer>
<FluentLabel>© My Company 2026</FluentLabel>
</Footer>
</PageLayout>Each area can be shown or hidden using the HeaderVisible, NavigationVisible, and FooterVisible parameters. When hidden, the area is removed from the grid layout entirely.
<PageLayout
headerVisible
navigationVisible={false}
footerVisible
header="Page Title"
body="Content without side navigation"
footer="Footer"
/><PageLayout HeaderVisible="true"
NavigationVisible="false"
FooterVisible="true">
<Header>Page Title</Header>
<Body>Content without side navigation</Body>
<Footer>Footer</Footer>
</PageLayout>Each area supports both a Class and Style parameter (e.g. HeaderClass, HeaderStyle, BodyClass, BodyStyle, etc.) for custom styling. The top-level container also supports Class and Style.
<PageLayout
className="my-page"
style={{ minHeight: '100vh' }}
headerClassName="my-header"
headerStyle={{ backgroundColor: 'var(--colorNeutralBackground5)' }}
bodyClassName="my-body"
bodyStyle={{ padding: '2rem' }}
header="Styled Header"
body="Styled Body"
/><PageLayout HeaderClass="my-header"
HeaderStyle="background-color: var(--neutral-layer-4);"
BodyClass="my-body"
BodyStyle="padding: 2rem;"
Class="my-page"
Style="min-height: 100vh;">
<Header>Styled Header</Header>
<Body>Styled Body</Body>
</PageLayout>A PageLayout can be nested inside the Body of another PageLayout to create sub-page layouts. The child layout automatically detects the parent and applies a child-layout CSS class with reduced header border styling.
// Nesting works the same — pass an inner <PageLayout> as the body slot.
// PageLayoutNestingContext auto-detects the parent and applies the
// child-layout variant (thinner header border, fills the parent's body
// area instead of the viewport).
<PageLayout
header="Main Header"
body={
<PageLayout
header={<SaveContextButton />}
body={/* sub-page content */}
/>
}
/><PageLayout>
<Header>Main Header</Header>
<Body>
<PageLayout>
<Header>
<MenuBar>
<SaveContextButton />
</MenuBar>
</Header>
<Body>
<!-- Sub-page content -->
</Body>
</PageLayout>
</Body>
</PageLayout>The body area scrolls independently from the header and footer, keeping them fixed in place. When navigating between pages, the body automatically scrolls to the top. The navigation area also scrolls independently.
The following example shows a PageLayout with all four areas populated.
Use the controls below to toggle each area's visibility and tune the resizable-navigation behavior — turn the splitter on/off, opt out of the localStorage persistence, and adjust the default / minimum / maximum drag widths. Drag the handle on the navigation/body boundary to resize, or double-click it to reset to the default width.
Name | Type | Default | Description |
|---|---|---|---|
Body | RenderFragment? | Content rendered in the main body area. This is the primary content region and scrolls independently from the header, footer, and navigation. Padding is applied automatically. | |
BodyClass | string? | CSS class applied to the body element. | |
BodyStyle | string? | Inline CSS style applied to the body element. | |
DefaultNavigationWidth | int | 280 | Navigation width in px used before the user resizes, and on double-click reset. Defaults to |
EnableNavigationResizing | bool | True | When Layout.PageLayout.NavigationVisible is Layout.PageLayout.DefaultNavigationWidth. |
Footer | RenderFragment? | Content rendered in the footer area at the bottom of the layout. The footer spans the full width and remains fixed while the body scrolls. Typically contains copyright notices or status information. | |
FooterClass | string? | CSS class applied to the footer element. | |
FooterStyle | string? | Inline CSS style applied to the footer element. | |
FooterVisible | bool | True | Controls whether the footer area is rendered. When set to |
Header | RenderFragment? | Content rendered in the header area at the top of the layout. Typically contains a | |
HeaderClass | string? | CSS class applied to the header element. Use this to apply custom styling such as background colors or borders. | |
HeaderStyle | string? | Inline CSS style applied to the header element. | |
HeaderVisible | bool | True | Controls whether the header area is rendered. When set to |
MaxNavigationWidth | int | 480 | Largest the navigation column will ever grow to, in px. Defaults to Layout.PageLayout.EnableNavigationResizing is on, this is the upper bound the user can drag the splitter to; when it's off, this is the element's |
MinNavigationWidth | int | 200 | Smallest the navigation can be dragged to, in px. Defaults to |
Navigation | RenderFragment? | Content rendered in the navigation area to the left of the body. Scrolls independently from the body. Typically contains a | |
NavigationClass | string? | CSS class applied to the navigation element. | |
NavigationResizeHandleLabel | string | Resize navigation | Accessible label for the resize handle (rendered with |
NavigationResizeStorageKey | string | ppp-page-layout-nav-width | localStorage key the chosen navigation width persists under. Defaults to Layout.PageLayout can be shown. Ignored when Layout.PageLayout.PersistNavigationWidth is |
NavigationStyle | string? | Inline CSS style applied to the navigation element. | |
NavigationVisible | bool | True | Controls whether the navigation area is rendered. When set to |
PersistNavigationWidth | bool | True | When Layout.PageLayout.NavigationResizeStorageKey and restored on subsequent loads. Set to Layout.PageLayout.DefaultNavigationWidth. Only meaningful when Layout.PageLayout.EnableNavigationResizing is |
ValidationSummaryVisible | bool | True | Controls whether the validation summary is displayed. Defaults to |
BodyBodyClassBodyStyleDefaultNavigationWidthEnableNavigationResizingLayout.PageLayout.NavigationVisible is Layout.PageLayout.DefaultNavigationWidth.FooterFooterClassFooterStyleFooterVisibleHeaderHeaderClassHeaderStyleHeaderVisibleMaxNavigationWidthLayout.PageLayout.EnableNavigationResizing is on, this is the upper bound the user can drag the splitter to; when it's off, this is the element's MinNavigationWidthNavigationNavigationClassNavigationResizeHandleLabelNavigationResizeStorageKeyLayout.PageLayout can be shown. Ignored when Layout.PageLayout.PersistNavigationWidth is NavigationStyleNavigationVisiblePersistNavigationWidthLayout.PageLayout.NavigationResizeStorageKey and restored on subsequent loads. Set to Layout.PageLayout.DefaultNavigationWidth. Only meaningful when Layout.PageLayout.EnableNavigationResizing is ValidationSummaryVisible