Stack
Stack
Stack은 요소를 세로 또는 가로로 배열하기 위한 컨테이너 컴포넌트예요.
출처: 문서
본문
Introduction (소개)
Stack 컴포넌트는 바로 아래 자식(immediate children)들의 레이아웃을 세로 또는 가로 축을 따라 관리하며, 각 자식 사이에 선택적으로 간격(spacing)과 구분선(divider)을 넣을 수 있어요.
:::info Stack은 1차원 레이아웃에 이상적이며, 세로 그리고 가로 배열이 모두 필요할 때는 Grid가 더 적합해요. :::
Basics (기본)
import Stack from '@mui/material/Stack';
Stack 컴포넌트는 배열할 요소들을 감싸는 범용 컨테이너 역할을 해요.
spacing prop을 사용해서 자식들 사이의 간격을 제어해요. spacing 값은 소수를 포함한 어떤 숫자든, 또는 문자열이 될 수 있어요. (이 prop은 theme.spacing() 헬퍼를 사용해 CSS 속성으로 변환돼요.)
import Box from '@mui/material/Box';
import Paper from '@mui/material/Paper';
import Stack from '@mui/material/Stack';
import { styled } from '@mui/material/styles';
const Item = styled(Paper)(({ theme }) => ({
backgroundColor: '#fff',
...theme.typography.body2,
padding: theme.spacing(1),
textAlign: 'center',
color: (theme.vars ?? theme).palette.text.secondary,
...theme.applyStyles('dark', {
backgroundColor: '#1A2027',
}),
}));
export default function BasicStack() {
return (
<Box sx={{ width: '100%' }}>
<Stack spacing={2}>
<Item>Item 1</Item>
<Item>Item 2</Item>
<Item>Item 3</Item>
</Stack>
</Box>
);
}
Stack vs. Grid
Stack은 1차원 레이아웃을 다루고, Grid는 2차원 레이아웃을 처리해요. 기본 방향은 column으로, 자식들을 세로로 쌓아요.
Direction (방향)
기본적으로 Stack은 항목들을 세로로(column) 배열해요. direction prop을 사용해서 항목들을 가로로(row) 배치할 수 있어요:
import Paper from '@mui/material/Paper';
import Stack from '@mui/material/Stack';
import { styled } from '@mui/material/styles';
const Item = styled(Paper)(({ theme }) => ({
backgroundColor: '#fff',
...theme.typography.body2,
padding: theme.spacing(1),
textAlign: 'center',
color: (theme.vars ?? theme).palette.text.secondary,
...theme.applyStyles('dark', {
backgroundColor: '#1A2027',
}),
}));
export default function DirectionStack() {
return (
<div>
<Stack direction="row" spacing={2}>
<Item>Item 1</Item>
<Item>Item 2</Item>
<Item>Item 3</Item>
</Stack>
</div>
);
}
Dividers (구분선)
divider prop을 사용해서 각 자식 사이에 요소를 삽입할 수 있어요. 이는 아래처럼 Divider 컴포넌트와 함께 특히 잘 동작해요:
import Divider from '@mui/material/Divider';
import Paper from '@mui/material/Paper';
import Stack from '@mui/material/Stack';
import { styled } from '@mui/material/styles';
const Item = styled(Paper)(({ theme }) => ({
backgroundColor: '#fff',
...theme.typography.body2,
padding: theme.spacing(1),
textAlign: 'center',
color: (theme.vars ?? theme).palette.text.secondary,
...theme.applyStyles('dark', {
backgroundColor: '#1A2027',
}),
}));
export default function DividerStack() {
return (
<div>
<Stack
direction="row"
divider={<Divider orientation="vertical" flexItem />}
spacing={2}
>
<Item>Item 1</Item>
<Item>Item 2</Item>
<Item>Item 3</Item>
</Stack>
</div>
);
}
Responsive values (반응형 값)
활성 브레이크포인트에 따라 direction 또는 spacing 값을 전환할 수 있어요.
import Paper from '@mui/material/Paper';
import Stack from '@mui/material/Stack';
import { styled } from '@mui/material/styles';
const Item = styled(Paper)(({ theme }) => ({
backgroundColor: '#fff',
...theme.typography.body2,
padding: theme.spacing(1),
textAlign: 'center',
color: (theme.vars ?? theme).palette.text.secondary,
...theme.applyStyles('dark', {
backgroundColor: '#1A2027',
}),
}));
export default function ResponsiveStack() {
return (
<div>
<Stack
direction={{ xs: 'column', sm: 'row' }}
spacing={{ xs: 1, sm: 2, md: 4 }}
>
<Item>Item 1</Item>
<Item>Item 2</Item>
<Item>Item 3</Item>
</Stack>
</div>
);
}
Flexbox gap
간격 구현에 flexbox gap을 사용하려면 useFlexGap prop을 true로 설정해요.
이렇게 하면 CSS 중첩 선택자를 사용하는 기본 구현의 알려진 제한 사항이 제거돼요. 다만 CSS flexbox gap은 일부 브라우저에서 완전히 지원되지 않아요.
사용하기 전에 지원 비율을 확인하는 것을 권장해요.
import Paper from '@mui/material/Paper';
import Stack from '@mui/material/Stack';
import Box from '@mui/material/Box';
import { styled } from '@mui/material/styles';
const Item = styled(Paper)(({ theme }) => ({
backgroundColor: '#fff',
...theme.typography.body2,
padding: theme.spacing(1),
textAlign: 'center',
color: (theme.vars ?? theme).palette.text.secondary,
flexGrow: 1,
...theme.applyStyles('dark', {
backgroundColor: '#1A2027',
}),
}));
export default function FlexboxGapStack() {
return (
<Box sx={{ width: 200 }}>
<Stack
spacing={{ xs: 1, sm: 2 }}
direction="row"
useFlexGap
sx={{ flexWrap: 'wrap' }}
>
<Item>Item 1</Item>
<Item>Item 2</Item>
<Item>Long content</Item>
</Stack>
</Box>
);
}
모든 Stack 인스턴스에 이 prop을 설정하려면 기본 props가 있는 테마를 만들어요:
import { ThemeProvider, createTheme } from '@mui/material/styles';
import Stack from '@mui/material/Stack';
const theme = createTheme({
components: {
MuiStack: {
defaultProps: {
useFlexGap: true,
},
},
},
});
function App() {
return (
<ThemeProvider theme={theme}>
<Stack>…</Stack> {/* uses flexbox gap by default */}
</ThemeProvider>
);
}
Interactive demo (대화형 데모)
아래는 다양한 설정의 시각적 결과를 살펴볼 수 있는 대화형 데모예요:
import * as React from 'react';
import FormControl from '@mui/material/FormControl';
import FormLabel from '@mui/material/FormLabel';
import FormControlLabel from '@mui/material/FormControlLabel';
import Grid from '@mui/material/Grid';
import Paper from '@mui/material/Paper';
import RadioGroup from '@mui/material/RadioGroup';
import Radio from '@mui/material/Radio';
import Stack, { StackProps } from '@mui/material/Stack';
import { HighlightedCode } from '@mui/internal-core-docs/HighlightedCode';
export default function InteractiveStack() {
const [direction, setDirection] = React.useState<StackProps['direction']>('row');
const [justifyContent, setJustifyContent] = React.useState('center');
const [alignItems, setAlignItems] = React.useState('center');
const [spacing, setSpacing] = React.useState(2);
const jsx = `
<Stack
direction="${direction}"
spacing={${spacing}}
sx={{
justifyContent: "${justifyContent}",
alignItems: "${alignItems}",
}}
>
`;
return (
<Stack sx={{ flexGrow: 1 }}>
<Stack
direction={direction}
spacing={spacing}
sx={{ justifyContent, alignItems, height: 240 }}
>
{[0, 1, 2].map((value) => (
<Paper
key={value}
sx={(theme) => ({
p: 2,
pt: value + 1,
pb: value + 1,
color: 'text.secondary',
typography: 'body2',
backgroundColor: '#fff',
...theme.applyStyles('dark', {
backgroundColor: '#1A2027',
}),
})}
>
{`Item ${value + 1}`}
</Paper>
))}
</Stack>
<Paper sx={{ p: 2 }}>
<Grid container spacing={3}>
<Grid size={12}>
<FormControl component="fieldset">
<FormLabel component="legend">direction</FormLabel>
<RadioGroup
row
name="direction"
aria-label="direction"
value={direction}
onChange={(event) => {
setDirection(event.target.value as StackProps['direction']);
}}
>
<FormControlLabel value="row" control={<Radio />} label="row" />
<FormControlLabel
value="row-reverse"
control={<Radio />}
label="row-reverse"
/>
<FormControlLabel
value="column"
control={<Radio />}
label="column"
/>
<FormControlLabel
value="column-reverse"
control={<Radio />}
label="column-reverse"
/>
</RadioGroup>
</FormControl>
</Grid>
<Grid size={12}>
<FormControl component="fieldset">
<FormLabel component="legend">alignItems</FormLabel>
<RadioGroup
row
name="alignItems"
aria-label="align items"
value={alignItems}
onChange={(event) => {
setAlignItems(event.target.value);
}}
>
<FormControlLabel
value="flex-start"
control={<Radio />}
label="flex-start"
/>
<FormControlLabel
value="center"
control={<Radio />}
label="center"
/>
<FormControlLabel
value="flex-end"
control={<Radio />}
label="flex-end"
/>
<FormControlLabel
value="stretch"
control={<Radio />}
label="stretch"
/>
<FormControlLabel
value="baseline"
control={<Radio />}
label="baseline"
/>
</RadioGroup>
</FormControl>
</Grid>
<Grid size={12}>
<FormControl component="fieldset">
<FormLabel component="legend">justifyContent</FormLabel>
<RadioGroup
row
name="justifyContent"
aria-label="justifyContent"
value={justifyContent}
onChange={(event) => {
setJustifyContent(event.target.value);
}}
>
<FormControlLabel
value="flex-start"
control={<Radio />}
label="flex-start"
/>
<FormControlLabel
value="center"
control={<Radio />}
label="center"
/>
<FormControlLabel
value="flex-end"
control={<Radio />}
label="flex-end"
/>
<FormControlLabel
value="space-between"
control={<Radio />}
label="space-between"
/>
<FormControlLabel
value="space-around"
control={<Radio />}
label="space-around"
/>
<FormControlLabel
value="space-evenly"
control={<Radio />}
label="space-evenly"
/>
</RadioGroup>
</FormControl>
</Grid>
<Grid size={12}>
<FormControl component="fieldset">
<FormLabel component="legend">spacing</FormLabel>
<RadioGroup
row
name="spacing"
aria-label="spacing"
value={spacing.toString()}
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
setSpacing(Number((event.target as HTMLInputElement).value));
}}
>
{[0, 0.5, 1, 2, 3, 4, 8, 12].map((value) => (
<FormControlLabel
key={value}
value={value.toString()}
control={<Radio />}
label={value}
/>
))}
</RadioGroup>
</FormControl>
</Grid>
</Grid>
</Paper>
<HighlightedCode code={jsx} language="jsx" />
</Stack>
);
}
Customization (커스터마이징)
sx prop을 사용해서 MUI System 패키지에 노출된 모든 스타일 함수와 테마 인식 속성에 접근할 수 있는 CSS의 슈퍼셋으로 어떤 Stack 인스턴스든 빠르게 커스터마이즈할 수 있어요. 아래는 이 prop을 사용해서 항목을 가운데 정렬하는 방법의 예시예요:
<Stack sx={{ alignItems: 'center' }} />
Limitations (제한 사항)
Margin on the children (자식들의 마진)
자식들의 마진을 커스터마이즈하는 것은 기본적으로 지원되지 않아요.
예를 들어, 아래 Button 컴포넌트의 top-margin은 무시될 거예요.
<Stack>
<Button sx={{ marginTop: '30px' }}>...</Button>
</Stack>
:::success
이 제한을 극복하려면 useFlexGap prop을 true로 설정해서 CSS flexbox gap 구현으로 전환하세요.
이 제한에 대해 더 자세히 알고 싶다면 이 RFC를 방문해보세요. :::
white-space: nowrap
flex 아이템의 초기 설정은 min-width: auto예요. 이로 인해 자식이 white-space: nowrap;을 사용할 때 위치 지정 충돌이 발생해요. 다음과 같이 문제를 재현할 수 있어요:
<Stack direction="row">
<Typography noWrap>
아이템이 컨테이너 안에 머물게 하려면 min-width: 0을 설정해야 해요.
<Stack direction="row" sx={{ minWidth: 0 }}>
<Typography noWrap>
import Avatar from '@mui/material/Avatar';
import Box from '@mui/material/Box';
import Paper from '@mui/material/Paper';
import Stack from '@mui/material/Stack';
import { styled } from '@mui/material/styles';
import Typography from '@mui/material/Typography';
const Item = styled(Paper)(({ theme }) => ({
backgroundColor: '#fff',
...theme.typography.body2,
padding: theme.spacing(1),
textAlign: 'center',
color: (theme.vars ?? theme).palette.text.secondary,
maxWidth: 400,
...theme.applyStyles('dark', {
backgroundColor: '#1A2027',
}),
}));
const message = `Truncation should be conditionally applicable on this long line of text
as this is a much longer line than what the container can support.`;
export default function ZeroWidthStack() {
return (
<Box sx={{ flexGrow: 1, overflow: 'hidden', px: 3 }}>
<Item sx={{ my: 1, mx: 'auto', p: 2 }}>
<Stack spacing={2} direction="row" sx={{ alignItems: 'center' }}>
<Avatar>W</Avatar>
<Typography noWrap>{message}</Typography>
</Stack>
</Item>
<Item sx={{ my: 1, mx: 'auto', p: 2 }}>
<Stack spacing={2} direction="row" sx={{ alignItems: 'center' }}>
<Stack>
<Avatar>W</Avatar>
</Stack>
<Stack sx={{ minWidth: 0 }}>
<Typography noWrap>{message}</Typography>
</Stack>
</Stack>
</Item>
</Box>
);
}
Anatomy (구조)
Stack 컴포넌트는 단일 루트 <div> 요소로 구성돼요:
<div class="MuiStack-root">
<!-- Stack contents -->
</div>
PigmentStack API (PigmentStack API)
Demos (데모)
이 React 컴포넌트 사용에 대한 예제와 자세한 내용은 컴포넌트 데모 페이지를 방문해보세요:
Import (불러오기)
import PigmentStack from '@mui/material/PigmentStack';
// or
import { PigmentStack } from '@mui/material';
Props (속성)
| Name | Type | Default | Required | Description |
|---|---|---|---|---|
| children | node |
- | No | |
| direction | 'column-reverse' | 'column' | 'row-reverse' | 'row' | Array<'column-reverse' | 'column' | 'row-reverse' | 'row'> | { lg?: 'column-reverse' | 'column' | 'row-reverse' | 'row', md?: 'column-reverse' | 'column' | 'row-reverse' | 'row', sm?: 'column-reverse' | 'column' | 'row-reverse' | 'row', xl?: 'column-reverse' | 'column' | 'row-reverse' | 'row', xs?: 'column-reverse' | 'column' | 'row-reverse' | 'row' } |
'column' |
No | |
| divider | node |
- | No | |
| spacing | Array<number | string> | number | { lg?: number | string, md?: number | string, sm?: number | string, xl?: number | string, xs?: number | string } | string |
0 |
No | |
| sx | Array<func | object | bool> | func | object |
- | No | The system prop that allows defining system overrides as well as additional CSS styles. |
Note: The
refis forwarded to the root element.
Source code (소스 코드)
이 페이지에서 원하는 정보를 찾지 못했다면, 더 자세한 내용을 위해 컴포넌트 구현을 살펴보는 것을 고려해보세요.
Stack API (Stack API)
Demos (데모)
이 React 컴포넌트 사용에 대한 예제와 자세한 내용은 컴포넌트 데모 페이지를 방문해보세요:
Import (불러오기)
import Stack from '@mui/material/Stack';
// or
import { Stack } from '@mui/material';
Props (속성)
| Name | Type | Default | Required | Description |
|---|---|---|---|---|
| children | node |
- | No | |
| component | elementType |
- | No | |
| direction | 'column-reverse' | 'column' | 'row-reverse' | 'row' | Array<'column-reverse' | 'column' | 'row-reverse' | 'row'> | object |
'column' |
No | |
| divider | node |
- | No | |
| spacing | Array<number | string> | number | object | string |
0 |
No | |
| sx | Array<func | object | bool> | func | object |
- | No | The system prop that allows defining system overrides as well as additional CSS styles. |
| useFlexGap | bool |
false |
No |
Note: The
refis forwarded to the root element (HTMLDivElement).
Any other props supplied will be provided to the root element (native element).
Theme default props (테마 기본 props)
MuiStack을 사용해서 테마로 이 컴포넌트의 기본 props를 변경할 수 있어요.
Source code (소스 코드)
이 페이지에서 원하는 정보를 찾지 못했다면, 더 자세한 내용을 위해 컴포넌트 구현을 살펴보는 것을 고려해보세요.
더 알아보기 (Learn more)
- Grid 컴포넌트 — 2차원 레이아웃이 필요할 때
- Stack 컴포넌트 데모 — 사용 예시와 자세한 내용