AppShell
AppShell
헤더, 네비게이션 바, aside, 풋터가 있는 반응형 셸 레이아웃 컴포넌트예요. 앱의 공통적인 Header / Navbar / Footer / Aside 레이아웃 패턴을 만들 때 사용해요.
출처: 문서
본문
Examples
이 페이지는 문서만 포함해요. 모든 AppShell 컴포넌트는 고정 위치를 가지며, 예시는 별도의 문서 섹션에 포함돼 있어요.
Usage
AppShell은 공통적인 Header / Navbar / Footer / Aside 레이아웃 패턴을 만들 수 있는 레이아웃 컴포넌트예요. 모든 AppShell 컴포넌트는 position: fixed 스타일을 가져서 페이지와 함께 스크롤되지 않아요.
Basic AppShell example은 헤더와 내비게이션 바가 있어요. 내비게이션 바는 기본적으로 모바일에서 숨겨지고 버거 버튼으로 토글할 수 있어요.
import { AppShell, Burger } from '@mantine/core';
import { useDisclosure } from '@mantine/hooks';
function Demo() {
const [opened, { toggle }] = useDisclosure();
return (
<AppShell header={{ height: 60 }} navbar={{ width: 300, breakpoint: 'sm', collapsed: { mobile: !opened } }} padding="md">
<AppShell.Header>
<Burger opened={opened} onClick={toggle} hiddenFrom="sm" size="sm" />
Logo
</AppShell.Header>
<AppShell.Navbar>
Navbar
</AppShell.Navbar>
<AppShell.Main>
Main
</AppShell.Main>
</AppShell>
);
}
AppShell components
AppShell– 다른 모든 섹션을 감싸고 전체 레이아웃을 구성하는 루트 컴포넌트.AppShell.Header–headerprop으로 제어되는 상단 고정 헤더.AppShell.Navbar–navbarprop으로 제어되는 왼쪽 고정 내비게이션 바.AppShell.Aside–asideprop으로 제어되는 오른쪽 고정 aside.AppShell.Footer–footerprop으로 제어되는 하단 고정 풋터.AppShell.Main– 정적으로 위치하며 다른 섹션에 의해 오프셋되는 메인 콘텐츠 영역.AppShell.Section–AppShell.Navbar나AppShell.Aside안에서 콘텐츠를 그룹화하는 유틸리티로, 스크롤 영역에 유용.
Configuration
AppShell 컴포넌트는 해당 섹션을 구성하기 위해 header, footer, navbar, aside props를 받아요. 해당 컴포넌트를 사용하려면 이 props를 설정해야 해요. 예를 들어 AppShell.Header 컴포넌트를 사용하려면 AppShell 컴포넌트에 header prop을 설정해야 해요.
header와 footer 구성 객체는 같은 타입을 공유해요.
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와 aside 구성 객체 타입:
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
layout prop은 AppShell.Header/AppShell.Footer와 AppShell.Navbar/AppShell.Aside가 서로 상대적으로 어떻게 위치하는지 제어해요. alt와 default 값을 받아요.
alt–AppShell.Navbar/AppShell.Aside가 뷰포트 전체 높이를 차지하고,AppShell.Header/AppShell.Footer너비는 뷰포트 너비에서AppShell.Navbar와AppShell.Aside너비를 뺀 값과 같음default–AppShell.Navbar/AppShell.Aside높이가 뷰포트 높이에서AppShell.Header/AppShell.Footer높이를 뺀 값과 같고,AppShell.Header/AppShell.Footer는 뷰포트 전체 너비를 차지
Height configuration
header와 footer 구성 객체의 height 속성은 다음과 같이 동작해요.
- 숫자를 전달하면 값이 rem으로 변환되어 모든 뷰포트 크기에서 높이로 사용돼요.
- 뷰포트 너비에 따라 높이를 바꾸려면 브레이크포인트를 키로 가진 객체를 사용하세요. style props와 같은 방식으로 동작해요.
높이를 숫자로 사용한 예: height는 rem으로 변환되고 모든 뷰포트 크기에서 동일하게 유지돼요.
import { AppShell } from '@mantine/core';
function Demo() {
return (
<AppShell header={{ height: 60 }}>
<AppShell.Header>Header</AppShell.Header>
</AppShell>
);
}
높이를 브레이크포인트 객체로 사용한 예: height는 뷰포트 너비가 theme.breakpoints.sm보다 크고 theme.breakpoints.lg보다 작을 때 48이에요.
Width configuration
navbar와 aside 구성 객체의 width 속성은 다음과 같이 동작해요.
- 숫자를 전달하면 값이 rem으로 변환되어 뷰포트가
breakpoint보다 클 때 너비로 사용돼요. - 뷰포트 너비에 따라 너비를 바꾸려면 브레이크포인트를 키로 가진 객체를 사용하세요. style props와 같은 방식으로 동작해요. 뷰포트가
breakpoint보다 작을 때는 너비가 항상 100%라는 점에 주의하세요.
padding prop
padding prop은 AppShell.Main 컴포넌트의 패딩을 제어해요. AppShell.Header, AppShell.Navbar, AppShell.Aside, AppShell.Footer 컴포넌트의 오프셋 계산에도 이 패딩이 사용되므로 AppShell.Main에 직접 패딩을 설정하는 대신 이 prop을 사용하는 것이 중요해요.
padding prop은 style props와 같은 방식으로 동작하며 숫자, 문자열, 브레이크포인트를 키로 가진 객체를 받아요. theme.spacing 값을 참조하거나 유효한 CSS 값을 사용할 수 있어요.
Header offset configuration
header prop은 AppShell.Main 컴포넌트가 헤더 높이만큼 오프셋될지 제어하는 offset 속성을 포함해요. 스크롤 위치에 따라 AppShell.Header를 접을 때 특히 유용해요. 예를 들어 use-headroom 훅으로 사용자가 아래로 스크롤하면 헤더를 숨기고 위로 스크롤하면 보여줄 수 있어요 (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>
{/* Content */}
</AppShell.Main>
</AppShell>
);
}
Collapsed navbar/aside configuration
navbar와 aside props는 { mobile: boolean; desktop: boolean } 형식의 객체를 받는 collapsed 속성을 포함해요. 뷰포트 너비에 따라 접힌 상태를 다르게 구성할 수 있어요.
모바일과 데스크톱의 별도 접힘 상태가 있는 Example:
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
header={{ height: 60 }}
navbar={{
width: 300,
breakpoint: 'sm',
collapsed: { mobile: !mobileOpened, desktop: !desktopOpened },
}}
padding="md"
>
<AppShell.Header>Header</AppShell.Header>
<AppShell.Navbar>
Navbar
<Button onClick={toggleDesktop}>Toggle navbar</Button>
</AppShell.Navbar>
</AppShell>
);
}
withBorder prop
withBorder prop은 AppShell과 관련 섹션(AppShell.Header, AppShell.Navbar, AppShell.Aside, AppShell.Footer)에서 사용할 수 있어요. 기본적으로 withBorder prop은 true이며, 모든 컴포넌트는 AppShell.Main 인접 쪽에 테두리를 가져요. 예를 들어 AppShell.Header는 페이지 상단에 있으므로 아래쪽에 테두리를 갖고, AppShell.Navbar는 페이지 왼쪽에 있으므로 오른쪽에 테두리를 가져요.
모든 컴포넌트에서 테두리를 제거하려면 AppShell에 withBorder={false}를 설정하세요.
import { AppShell } from '@mantine/core';
// None of the components will have a border
function Demo() {
return (
<AppShell withBorder={false} header={{ height: 60 }}>
{/* AppShell content */}
</AppShell>
);
}
특정 컴포넌트에서만 테두리를 제거하려면 해당 컴포넌트에 withBorder={false}를 설정하세요.
import { AppShell } from '@mantine/core';
function Demo() {
return (
<AppShell header={{ height: 60 }}>
<AppShell.Header withBorder={false}>Header</AppShell.Header>
</AppShell>
);
}
zIndex prop
zIndex prop은 AppShell과 관련 섹션(AppShell.Header, AppShell.Navbar, AppShell.Aside, AppShell.Footer)에서 사용할 수 있어요. 기본적으로 모든 섹션은 z-index가 100이에요.
모든 섹션의 z-index를 바꾸려면 AppShell 컴포넌트에 zIndex prop을 설정하세요.
import { AppShell } from '@mantine/core';
// All sections will have z-index of 200
function Demo() {
return <AppShell zIndex={200} header={{ height: 60 }}>{/* AppShell content */}</AppShell>;
}
특정 섹션의 z-index를 바꾸려면 해당 섹션에 zIndex prop을 설정하세요.
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 header={{ height: 60 }} navbar={{ width: 300, breakpoint: 'sm' }} aside={{ width: 300, breakpoint: 'sm' }}>
<AppShell.Header>Header</AppShell.Header>
<AppShell.Navbar zIndex={300}>Navbar</AppShell.Navbar>
<AppShell.Aside zIndex={300}>Aside</AppShell.Aside>
</AppShell>
);
}
Control transitions
AppShell 컴포넌트에 transitionDuration과 transitionTimingFunction props를 사용해 섹션 애니메이션을 제어할 수 있어요.
disabled prop
AppShell 컴포넌트에 disabled prop을 설정하면 AppShell.Main을 제외한 모든 섹션의 렌더링을 막을 수 있어요. 애플리케이션의 특정 페이지에서 셸을 숨기고 싶을 때 유용해요.
AppShell.Section component
AppShell.Section은 AppShell.Navbar와 AppShell.Aside 안에 정리된 영역을 만드는 데 사용돼요. 이 컴포넌트들은 flex-direction: column인 flexbox 컨테이너이므로, grow prop이 있는 AppShell.Section 컴포넌트는 사용 가능한 공간을 채우도록 확장되고 component={ScrollArea}로 설정하면 스크롤 가능하게 만들 수 있어요.
다음 예시에서:
- 첫 번째와 마지막 섹션(헤더와 풋터)은 콘텐츠에 필요한 공간만 차지해요
grow가 있는 중간 섹션은 나머지 모든 공간을 차지하고 콘텐츠가 사용 가능한 높이를 초과하면 스크롤 가능해져요
import { AppShell, ScrollArea } from '@mantine/core';
function Demo() {
return (
<AppShell navbar={{ width: 300, breakpoint: 'sm' }} padding="md">
<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>
);
}
Semantic elements
중요: 페이지당 하나의 <main> 요소만 허용되므로 AppShell.Main 안에 <main> 요소를 사용하지 마세요.
| Component | Root HTML element |
|---|---|
AppShell.Header |
header |
AppShell.Footer |
footer |
AppShell.Main |
main |
AppShell.Navbar |
nav |
AppShell.Aside |
aside |
AppShell.Section |
div |
CSS variables
| Variable | Description |
|---|---|
--app-shell-navbar-width |
Navbar width |
--app-shell-navbar-offset |
Navbar offset |
--app-shell-aside-width |
Aside width |
--app-shell-aside-offset |
Aside offset |
--app-shell-header-height |
Header height |
--app-shell-header-offset |
Header offset |
--app-shell-footer-height |
Footer height |
--app-shell-footer-offset |
Footer offset |
스타일에서 CSS 변수를 사용하는 예시:
.main {
min-height: calc(100dvh - var(--app-shell-header-height));
}