apps/mantine.dev/src/pages/core/app-shell.mdx
import { Code } from '@mantine/core'; import { Layout } from '@/layout'; import { MDX_DATA } from '@/mdx';
export default Layout(MDX_DATA.AppShell);
This page contains documentation only. All AppShell components have a fixed
position; examples are included in a separate documentation section.
<ExamplesButton link="/app-shell?e=BasicAppShell" label="Open AppShell examples page" />
AppShell is a layout component that can be used to create a common Header / Navbar / Footer / Aside
layout pattern. All AppShell components have position: fixed styling, so they do not scroll with
the page.
Basic AppShell example with header and navbar. The navbar is hidden on mobile by default and can be toggled with the burger button.
import { AppShell, Burger } from '@mantine/core';
import { useDisclosure } from '@mantine/hooks';
function Demo() {
const [opened, { toggle }] = useDisclosure();
return (
<AppShell
padding="md"
header={{ height: 60 }}
navbar={{
width: 300,
breakpoint: 'sm',
collapsed: { mobile: !opened },
}}
>
<AppShell.Header>
<Burger
opened={opened}
onClick={toggle}
hiddenFrom="sm"
size="sm"
/>
<div>Logo</div>
</AppShell.Header>
<AppShell.Navbar>Navbar</AppShell.Navbar>
<AppShell.Main>Main</AppShell.Main>
</AppShell>
);
}
AppShell – root component that wraps all other sections and configures the overall layout.AppShell.Header – fixed header at the top, controlled by the header prop.AppShell.Navbar – fixed navbar on the left, controlled by the navbar prop.AppShell.Aside – fixed aside on the right, controlled by the aside prop.AppShell.Footer – fixed footer at the bottom, controlled by the footer prop.AppShell.Main – main content area, statically positioned and offset by the other sections.AppShell.Section – utility for grouping content inside AppShell.Navbar or AppShell.Aside, useful for scrollable areas.The AppShell component accepts header, footer, navbar, and aside props to configure the corresponding sections.
You must set these props if you want to use the corresponding components.
For example, to use the AppShell.Header component, you need to set the header prop on the AppShell component.
header and footer configuration objects share the same type:
interface Configuration {
/** Height of the section: number, string or
** object with breakpoints as keys and height as values */
height: AppShellSize | AppShellResponsiveSize;
/** When collapsed is true, the section is hidden
** from the viewport and doesn't affect the AppShell.Main offset */
collapsed?: boolean;
/** Controls whether AppShell.Main should be offset by this section.
** Useful for scenarios like hiding a header based on scroll position. */
offset?: boolean;
}
navbar and aside configuration objects type:
interface Configuration {
/** Width of the section: number, string, or
** object with breakpoints as keys and widths as values */
width: AppShellSize | AppShellResponsiveSize;
/** Breakpoint at which the section switches to mobile mode.
** In mobile mode, the section always has 100% width and its
** collapsed state is controlled by `collapsed.mobile`
** instead of `collapsed.desktop` */
breakpoint: MantineBreakpoint | (string & {}) | number;
/** Determines whether the section should be collapsed */
collapsed?: { desktop?: boolean; mobile?: boolean };
}
layout prop controls how AppShell.Header/AppShell.Footer and AppShell.Navbar/AppShell.Aside
are positioned relative to each other. It accepts alt and default values:
alt – AppShell.Navbar/AppShell.Aside extends the full viewport height, while AppShell.Header/AppShell.Footer width equals the viewport width minus the width of AppShell.Navbar and AppShell.Aside (example)default – AppShell.Navbar/AppShell.Aside height equals the viewport height minus AppShell.Header/AppShell.Footer height, and AppShell.Header/AppShell.Footer spans the full viewport width (example)The height property in header and footer configuration objects works as follows:
Example with height as a number: height is converted to rem,
and height remains the same at all viewport sizes:
import { AppShell } from '@mantine/core';
function Demo() {
return (
<AppShell header={{ height: 48 }}>
<AppShell.Header>Header</AppShell.Header>
</AppShell>
);
}
Example with height as an object with breakpoints:
height is 48 when viewport width is < theme.breakpoints.smheight is 60 when viewport width is >= theme.breakpoints.sm and < theme.breakpoints.lgheight is 76 when viewport width is >= theme.breakpoints.lgimport { AppShell } from '@mantine/core';
function Demo() {
return (
<AppShell header={{ height: { base: 48, sm: 60, lg: 76 } }}>
<AppShell.Header>Header</AppShell.Header>
</AppShell>
);
}
The width property in navbar and aside configuration objects works as follows:
breakpoint.breakpoint.Example with width as a number: width is converted to rem,
and width remains the same at viewport sizes larger than breakpoint.
The width is 100% when viewport width is less than breakpoint:
import { AppShell } from '@mantine/core';
function Demo() {
return (
<AppShell navbar={{ width: 48, breakpoint: 'sm' }}>
<AppShell.Navbar>Navbar</AppShell.Navbar>
</AppShell>
);
}
Example with width as an object with breakpoints:
width is 100% when viewport width is < theme.breakpoints.smwidth is 200 when viewport width is >= theme.breakpoints.sm and < theme.breakpoints.lgwidth is 300 when viewport width is >= theme.breakpoints.lgimport { AppShell } from '@mantine/core';
function Demo() {
return (
<AppShell
navbar={{ width: { sm: 200, lg: 300 }, breakpoint: 'sm' }}
>
<AppShell.Navbar>Navbar</AppShell.Navbar>
</AppShell>
);
}
The padding prop controls the padding of the AppShell.Main component. It's important to use this prop
instead of setting padding directly on AppShell.Main because this padding is also used to calculate offsets for
the AppShell.Header, AppShell.Navbar, AppShell.Aside, and AppShell.Footer components.
The padding prop works the same way as style props and
accepts numbers, strings, and objects with breakpoints as keys and padding values. You can
reference theme.spacing values or use any valid CSS value.
Example with static padding prop:
import { AppShell } from '@mantine/core';
function Demo() {
return <AppShell padding="md"></AppShell>;
}
Example with responsive padding prop:
padding is 10 when viewport width is < theme.breakpoints.smpadding is 15 when viewport width is >= theme.breakpoints.sm and < theme.breakpoints.lgpadding is theme.spacing.xl when viewport width is >= theme.breakpoints.lgimport { AppShell } from '@mantine/core';
function Demo() {
return (
<AppShell padding={{ base: 10, sm: 15, lg: 'xl' }}>
</AppShell>
);
}
The header prop includes an offset property that allows you to control
whether the AppShell.Main component is offset by the header's height.
This is particularly useful when you want to collapse the AppShell.Header
based on scroll position. For example, you can use the use-headroom
hook to hide the header when the user scrolls down and show it when they
scroll up (example).
import { AppShell, rem } from '@mantine/core';
import { useHeadroom } from '@mantine/hooks';
function Demo() {
const { pinned } = useHeadroom({ fixedAt: 120 });
return (
<AppShell
header={{ height: 60, collapsed: !pinned, offset: false }}
padding="md"
>
<AppShell.Header>Header</AppShell.Header>
<AppShell.Main
pt={`calc(${rem(60)} + var(--mantine-spacing-md))`}
>
</AppShell.Main>
</AppShell>
);
}
The navbar and aside props include a collapsed property that accepts an object with the format { mobile: boolean; desktop: boolean }.
This allows you to configure the collapsed state differently based on viewport width.
Example with separate collapsed states for mobile and desktop:
import { AppShell, Button } from '@mantine/core';
import { useDisclosure } from '@mantine/hooks';
export function CollapseDesktop() {
const [mobileOpened, { toggle: toggleMobile }] = useDisclosure();
const [desktopOpened, { toggle: toggleDesktop }] =
useDisclosure(true);
return (
<AppShell
padding="md"
header={{ height: 60 }}
navbar={{
width: 300,
breakpoint: 'sm',
collapsed: { mobile: !mobileOpened, desktop: !desktopOpened },
}}
>
<AppShell.Header>Header</AppShell.Header>
<AppShell.Navbar>Navbar</AppShell.Navbar>
<AppShell.Main>
<Button onClick={toggleDesktop} visibleFrom="sm">
Toggle navbar
</Button>
<Button onClick={toggleMobile} hiddenFrom="sm">
Toggle navbar
</Button>
</AppShell.Main>
</AppShell>
);
}
The withBorder prop is available on AppShell and associated sections: AppShell.Header, AppShell.Navbar, AppShell.Aside and AppShell.Footer.
By default, withBorder prop is true – all components have a border on the side that is adjacent to the AppShell.Main component.
For example, AppShell.Header is located at the top of the page – it has a border on the bottom side,
AppShell.Navbar is located on the left side of the page – it has a border on the right side.
To remove the border from all components, set withBorder={false} on the AppShell:
import { AppShell } from '@mantine/core';
// None of the components will have a border
function Demo() {
return (
<AppShell withBorder={false}></AppShell>
);
}
To remove the border from a specific component, set withBorder={false} on that component:
import { AppShell } from '@mantine/core';
function Demo() {
return (
<AppShell>
<AppShell.Header withBorder={false}>Header</AppShell.Header>
</AppShell>
);
}
The zIndex prop is available on AppShell and its associated sections: AppShell.Header, AppShell.Navbar, AppShell.Aside, and AppShell.Footer.
By default, all sections have a z-index of 100.
To change the z-index of all sections, set the zIndex prop on the AppShell component:
import { AppShell } from '@mantine/core';
// All sections will have z-index of 200
function Demo() {
return <AppShell zIndex={200}></AppShell>;
}
To change z-index of a specific section, set zIndex prop on that section:
import { AppShell } from '@mantine/core';
// AppShell.Header has z-index of 100
// AppShell.Navbar and AppShell.Aside have z-index of 300
function Demo() {
return (
<AppShell>
<AppShell.Header zIndex={100}>Header</AppShell.Header>
<AppShell.Navbar zIndex={300}>Navbar</AppShell.Navbar>
<AppShell.Aside zIndex={300}>Aside</AppShell.Aside>
</AppShell>
);
}
Use the transitionDuration and transitionTimingFunction props on the AppShell component to control section animations:
import { AppShell } from '@mantine/core';
function Demo() {
return (
<AppShell
transitionDuration={500}
transitionTimingFunction="ease"
>
</AppShell>
);
}
Set the disabled prop on the AppShell component to prevent all sections except AppShell.Main from rendering.
This is useful when you want to hide the shell on certain pages of your application.
import { AppShell } from '@mantine/core';
function Demo() {
return <AppShell disabled></AppShell>;
}
AppShell.Section is used to create organized areas within AppShell.Navbar and AppShell.Aside.
Since these components are flexbox containers with flex-direction: column, the AppShell.Section
component with the grow prop will expand to fill the available space and can be made scrollable by setting
component={ScrollArea}.
In the following example:
grow takes all the remaining space and becomes scrollable when content exceeds the available heightimport { AppShell, ScrollArea } from '@mantine/core';
function Demo() {
return (
<AppShell navbar={{ width: 300, breakpoint: 0 }}>
<AppShell.Navbar>
<AppShell.Section>Navbar header</AppShell.Section>
<AppShell.Section grow component={ScrollArea}>
Navbar main section that will expand to fill available space
</AppShell.Section>
<AppShell.Section>
Navbar footer – always at the bottom
</AppShell.Section>
</AppShell.Navbar>
<AppShell.Main>Main</AppShell.Main>
</AppShell>
);
}
Important: do not use a <main> element inside AppShell.Main, as only one <main> element is allowed per page.
<DataTable head={['Component', 'Root HTML element']} data={[ [<Code>AppShell.Header</Code>, <Code>header</Code>], [<Code>AppShell.Footer</Code>, <Code>footer</Code>], [<Code>AppShell.Main</Code>, <Code>main</Code>], [<Code>AppShell.Navbar</Code>, <Code>nav</Code>], [<Code>AppShell.Aside</Code>, <Code>aside</Code>], [<Code>AppShell.Section</Code>, <Code>div</Code>], ]} />
<DataTable head={['Variable', 'Description']} data={[ [<Code>--app-shell-navbar-width</Code>, 'Navbar width'], [<Code>--app-shell-navbar-offset</Code>, 'Navbar offset'], [<Code>--app-shell-aside-width</Code>, 'Aside width'], [<Code>--app-shell-aside-offset</Code>, 'Aside offset'], [<Code>--app-shell-header-height</Code>, 'Header height'], [<Code>--app-shell-header-offset</Code>, 'Header offset'], [<Code>--app-shell-footer-height</Code>, 'Footer height'], [<Code>--app-shell-footer-offset</Code>, 'Footer offset'], ]} />
Example of using CSS variables in styles:
.main {
min-height: calc(100dvh - var(--app-shell-header-height));
}