AppShell
Responsive shell for your application with header, navbar, aside and footer
Source
LLM docs
Docs
Package
Examples
This page contains documentation only. All AppShell components have a fixed
position; examples are included in a separate documentation section.
Usage
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.
AppShell components
AppShell– root component that wraps all other sections and configures the overall layout.AppShell.Header– fixed header at the top, controlled by theheaderprop.AppShell.Navbar– fixed navbar on the left, controlled by thenavbarprop.AppShell.Aside– fixed aside on the right, controlled by theasideprop.AppShell.Footer– fixed footer at the bottom, controlled by thefooterprop.AppShell.Main– main content area, statically positioned and offset by the other sections.AppShell.Section– utility for grouping content insideAppShell.NavbarorAppShell.Aside, useful for scrollable areas.
Configuration
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:
navbar and aside configuration objects type:
layout prop
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.Asideextends the full viewport height, whileAppShell.Header/AppShell.Footerwidth equals the viewport width minus the width ofAppShell.NavbarandAppShell.Aside(example)default–AppShell.Navbar/AppShell.Asideheight equals the viewport height minusAppShell.Header/AppShell.Footerheight, andAppShell.Header/AppShell.Footerspans the full viewport width (example)
Resizable sections
Use the useAppShellResize hook together with the resize prop to let the user resize
AppShell.Navbar/AppShell.Aside horizontally and AppShell.Header/AppShell.Footer vertically –
with a mouse, touch, or keyboard. Only the sections passed to useAppShellResize get a resize
handle, the section configuration objects (navbar={{ width, breakpoint }} etc.) do not change:
In this example, navbar and header are passed to useAppShellResize, so AppShell.Navbar and
AppShell.Header render a resize handle – AppShell.Aside and AppShell.Footer keep their
configured size even if they are used elsewhere in the same AppShell. See the
resizable app shell example for a full demo that includes a
reset button.
Note that when the resize prop is set (same as with mode="static"), the --app-shell-*
CSS variables are defined on the AppShell root element instead of :root, so
they can only be read from elements rendered inside the AppShell.
useAppShellResize hook
The hook accepts an object with the following properties:
Each section option (navbar, aside, header, footer) accepts the same shape:
Note that the effective maximum is always the smaller of max and the current viewport size – a
max larger than the viewport has no effect.
Resize handles are rendered as role="separator" elements with an English aria-label by default
(Resize navbar, Resize aside, Resize header, Resize footer). Use the label option to
localize it or to describe the section more precisely, for example
navbar: { min: 200, max: 500, label: 'Sidebar width' }.
useAppShellResize returns a controller for every section (navbar, aside, header, footer)
with the following shape:
as well as sizes – current size overrides of all sections, and resetAll() – clears size
overrides of all sections. setSize, reset, and resetAll never call onResize/onResizeEnd.
The returned object also carries a few fields used internally by AppShell to wire up the resize
handles (options, activeSection, startResize, previewResize, endResize,
initCollapse, reportCollapse) – you do not need to use them directly.
Keyboard shortcuts
Every resize handle is focusable and supports the following keys. Keys for the section's other
axis (for example, ArrowUp/ArrowDown on AppShell.Navbar) are ignored:
Arrow key, Home, and End presses commit the size immediately, so they trigger onResizeEnd,
not onResize. Double-clicking a handle resets the section, same as calling reset().
Responsive sizes and reset
Once a section is resized, it stops following its configured width/height – including a
responsive { base, sm, ... } object – until reset() is called. Calling reset() (or
double-clicking the handle) returns the section to the value currently defined in its
width/height configuration.
Persisting sizes
Combine useAppShellResize with use-local-storage to remember sizes
between visits:
initialSizes is applied to a section only until that section is first resized. This is what
makes the pattern above work with useLocalStorage – its stored value is only available after the
initial render (getInitialValueInEffect: true by default), and by the time it arrives the
section has not been touched yet, so it is still picked up.
reset() and resetAll() never call onResize/onResizeEnd, so calling them does not clear a
size that was already written to storage by a previous onResizeEnd call – clear the stored value
yourself in whatever reset handler your application uses alongside reset()/resetAll().
Drag-to-collapse
Set collapseThreshold on a section to have onCollapseChange report when a drag crosses that
size:
onCollapseChange only reports the crossing – your application still owns the collapsed prop of
AppShell.Navbar/AppShell.Aside/AppShell.Header/AppShell.Footer. Note that a collapsed
AppShell.Navbar/AppShell.Aside moves its resize handle off screen together with the rest of the
section, so pair drag-to-collapse with a Burger (or another control) rather than relying on the
handle as the way to bring the section back.
Hiding the resize handle
Resize handles are hidden below the AppShell.Navbar/AppShell.Aside breakpoint, and while the
corresponding section is collapsed.
Height configuration
The height property in header and footer configuration objects works as follows:
- If you pass a number, the value will be converted to rem and used as the height at all viewport sizes.
- To change height based on viewport width, use an object with breakpoints as keys and height as values. This works the same way as style props.
Example with height as a number: height is converted to rem,
and height remains the same at all viewport sizes:
Example with height as an object with breakpoints:
heightis 48 when viewport width is <theme.breakpoints.smheightis 60 when viewport width is >=theme.breakpoints.smand <theme.breakpoints.lgheightis 76 when viewport width is >=theme.breakpoints.lg
Width configuration
The width property in navbar and aside configuration objects works as follows:
- If you pass a number, the value will be converted to rem and used as the
width when the viewport is larger than
breakpoint. - To change width based on viewport width, use an object with breakpoints as keys and width as
values. This works the same way as style props.
Note that width is always 100% when the viewport is smaller than
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:
Example with width as an object with breakpoints:
widthis 100% when viewport width is <theme.breakpoints.smwidthis 200 when viewport width is >=theme.breakpoints.smand <theme.breakpoints.lgwidthis 300 when viewport width is >=theme.breakpoints.lg
padding prop
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:
Example with responsive padding prop:
paddingis 10 when viewport width is <theme.breakpoints.smpaddingis 15 when viewport width is >=theme.breakpoints.smand <theme.breakpoints.lgpaddingistheme.spacing.xlwhen viewport width is >=theme.breakpoints.lg
Header offset configuration
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).
Collapsed navbar/aside configuration
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:
withBorder prop
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:
To remove the border from a specific component, set withBorder={false} on that component:
zIndex prop
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:
To change z-index of a specific section, set zIndex prop on that section:
Control transitions
Use the transitionDuration and transitionTimingFunction props on the AppShell component to control section animations:
disabled prop
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.
AppShell.Section component
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:
- The first and last sections (header and footer) take only the space needed for their content
- The middle section with
growtakes all the remaining space and becomes scrollable when content exceeds the available height
Semantic elements
Important: do not use a <main> element inside AppShell.Main, as only one <main> element is allowed per page.
CSS variables
Example of using CSS variables in styles:
These variables are defined on :root by default, but on the AppShell root element when the
resize prop is set or mode="static" is used – in these cases they are not available to elements
rendered outside of the AppShell, for example, in a Modal or Drawer rendered within a Portal.