Select
Select (셀렉트/선택)
Select 컴포넌트는 옵션 목록에서 사용자가 제공한 정보를 수집하는 데 사용해요.
출처: 문서
본문
기본 select (Basic select)
메뉴는 뷰포트(viewpoint) 하단 근처에 있지 않은 한, 이를 발생시킨 요소 아래에 배치돼요.
import * as React from 'react';
import Box from '@mui/material/Box';
import InputLabel from '@mui/material/InputLabel';
import MenuItem from '@mui/material/MenuItem';
import FormControl from '@mui/material/FormControl';
import Select, { SelectChangeEvent } from '@mui/material/Select';
export default function BasicSelect() {
const [age, setAge] = React.useState('');
const handleChange = (event: SelectChangeEvent) => {
setAge(event.target.value as string);
};
return (
<Box sx={{ minWidth: 120 }}>
<FormControl fullWidth>
<InputLabel id="demo-simple-select-label">Age</InputLabel>
<Select
labelId="demo-simple-select-label"
id="demo-simple-select"
value={age}
label="Age"
onChange={handleChange}
>
<MenuItem value={10}>Ten</MenuItem>
<MenuItem value={20}>Twenty</MenuItem>
<MenuItem value={30}>Thirty</MenuItem>
</Select>
</FormControl>
</Box>
);
}
고급 기능 (Advanced features)
Select 컴포넌트는 네이티브 <select> 요소와 상호 교환할 수 있게 설계되었어요.
combobox, multiselect, autocomplete, async 또는 creatable 지원 같은 더 고급 기능을 찾고 있다면 Autocomplete 컴포넌트로 가 보세요. 이는 "react-select"와 "downshift" 패키지의 개선된 버전을 지향해요.
Props
Select 컴포넌트는 InputBase의 커스텀 <input> 요소로 구현돼요. 선택한 variant에 따라 text field 컴포넌트의 하위 컴포넌트 — OutlinedInput, Input, FilledInput 중 하나를 확장해요. 동일한 스타일과 많은 props를 공유하므로, 각 컴포넌트의 API 페이지를 참고하세요.
:::warning
입력 컴포넌트와 달리 placeholder prop은 Select에서 사용할 수 없어요. placeholder를 추가하려면 아래의 placeholder 섹션을 참고하세요.
:::
변형 (Variants)
import * as React from 'react';
import InputLabel from '@mui/material/InputLabel';
import MenuItem from '@mui/material/MenuItem';
import FormControl from '@mui/material/FormControl';
import Select, { SelectChangeEvent } from '@mui/material/Select';
export default function SelectVariants() {
const [age, setAge] = React.useState('');
const handleChange = (event: SelectChangeEvent) => {
setAge(event.target.value);
};
return (
<div>
<FormControl variant="outlined" sx={{ m: 1, minWidth: 120 }}>
<InputLabel id="demo-simple-select-outlined-label">Age</InputLabel>
<Select
labelId="demo-simple-select-outlined-label"
id="demo-simple-select-outlined"
value={age}
onChange={handleChange}
label="Age"
>
<MenuItem value="">
<em>None</em>
</MenuItem>
<MenuItem value={10}>Ten</MenuItem>
<MenuItem value={20}>Twenty</MenuItem>
<MenuItem value={30}>Thirty</MenuItem>
</Select>
</FormControl>
<FormControl variant="standard" sx={{ m: 1, minWidth: 120 }}>
<InputLabel id="demo-simple-select-standard-label">Age</InputLabel>
<Select
labelId="demo-simple-select-standard-label"
id="demo-simple-select-standard"
value={age}
onChange={handleChange}
>
<MenuItem value="">
<em>None</em>
</MenuItem>
<MenuItem value={10}>Ten</MenuItem>
<MenuItem value={20}>Twenty</MenuItem>
<MenuItem value={30}>Thirty</MenuItem>
</Select>
</FormControl>
<FormControl variant="filled" sx={{ m: 1, minWidth: 120 }}>
<InputLabel id="demo-simple-select-filled-label">Age</InputLabel>
<Select
labelId="demo-simple-select-filled-label"
id="demo-simple-select-filled"
value={age}
onChange={handleChange}
>
<MenuItem value="">
<em>None</em>
</MenuItem>
<MenuItem value={10}>Ten</MenuItem>
<MenuItem value={20}>Twenty</MenuItem>
<MenuItem value={30}>Thirty</MenuItem>
</Select>
</FormControl>
</div>
);
}
:::warning
Select의 outlined variant와 함께 FormControl을 사용할 때는 label을 두 곳에 제공해야 한다는 점에 유의하세요: InputLabel 컴포넌트와 Select 컴포넌트의 label prop (위 데모 참고). 이것은 label이 떠오르는(float) 애니메이션이 제대로 동작하는 데 필요해요.
:::
라벨과 도우미 텍스트 (Labels and helper text)
Select에는 항상 접근 가능한 이름(accessible name)이 필요해요. 이 이름은 labelId로 Select에 연결된 보이는 라벨(예: InputLabel)이나, 입력 요소 props(inputProps)에 aria-label prop을 추가하는 방식으로 제공할 수 있어요. 더 많은 정보가 필요하다면 도우미 텍스트 요소를 제공하고 aria-describedby를 사용해 Select에 연결하세요.
import * as React from 'react';
import InputLabel from '@mui/material/InputLabel';
import MenuItem from '@mui/material/MenuItem';
import FormHelperText from '@mui/material/FormHelperText';
import FormControl from '@mui/material/FormControl';
import Select, { SelectChangeEvent } from '@mui/material/Select';
export default function SelectLabels() {
const id = React.useId();
const noLabelId = React.useId();
const [age, setAge] = React.useState('');
const handleChange = (event: SelectChangeEvent) => {
setAge(event.target.value);
};
return (
<div>
<FormControl sx={{ m: 1, minWidth: 120 }}>
<InputLabel id={`${id}-label`}>Age</InputLabel>
<Select
aria-describedby={`${id}-helper-text`}
labelId={`${id}-label`}
id={id}
value={age}
label="Age"
onChange={handleChange}
>
<MenuItem value="">
<em>None</em>
</MenuItem>
<MenuItem value={10}>Ten</MenuItem>
<MenuItem value={20}>Twenty</MenuItem>
<MenuItem value={30}>Thirty</MenuItem>
</Select>
<FormHelperText id={`${id}-helper-text`}>
Visible label and helper text
</FormHelperText>
</FormControl>
<FormControl sx={{ m: 1, minWidth: 120 }}>
<Select
aria-describedby={`${noLabelId}-helper-text`}
value={age}
onChange={handleChange}
displayEmpty
inputProps={{ 'aria-label': 'Age' }}
>
<MenuItem value="">
<em>None</em>
</MenuItem>
<MenuItem value={10}>Ten</MenuItem>
<MenuItem value={20}>Twenty</MenuItem>
<MenuItem value={30}>Thirty</MenuItem>
</Select>
<FormHelperText id={`${noLabelId}-helper-text`}>
aria-label and helper text
</FormHelperText>
</FormControl>
</div>
);
}
자동 너비 (Auto width)
import * as React from 'react';
import InputLabel from '@mui/material/InputLabel';
import MenuItem from '@mui/material/MenuItem';
import FormControl from '@mui/material/FormControl';
import Select, { SelectChangeEvent } from '@mui/material/Select';
export default function SelectAutoWidth() {
const [age, setAge] = React.useState('');
const handleChange = (event: SelectChangeEvent) => {
setAge(event.target.value);
};
return (
<div>
<FormControl sx={{ m: 1, minWidth: 80 }}>
<InputLabel id="demo-simple-select-autowidth-label">Age</InputLabel>
<Select
labelId="demo-simple-select-autowidth-label"
id="demo-simple-select-autowidth"
value={age}
onChange={handleChange}
autoWidth
label="Age"
>
<MenuItem value="">
<em>None</em>
</MenuItem>
<MenuItem value={20}>Twenty</MenuItem>
<MenuItem value={21}>Twenty one</MenuItem>
<MenuItem value={22}>Twenty one and a half</MenuItem>
</Select>
</FormControl>
</div>
);
}
작은 크기 (Small Size)
import * as React from 'react';
import InputLabel from '@mui/material/InputLabel';
import MenuItem from '@mui/material/MenuItem';
import FormControl from '@mui/material/FormControl';
import Select, { SelectChangeEvent } from '@mui/material/Select';
export default function SelectSmall() {
const [age, setAge] = React.useState('');
const handleChange = (event: SelectChangeEvent) => {
setAge(event.target.value);
};
return (
<FormControl sx={{ m: 1, minWidth: 120 }} size="small">
<InputLabel id="demo-select-small-label">Age</InputLabel>
<Select
labelId="demo-select-small-label"
id="demo-select-small"
value={age}
label="Age"
onChange={handleChange}
>
<MenuItem value="">
<em>None</em>
</MenuItem>
<MenuItem value={10}>Ten</MenuItem>
<MenuItem value={20}>Twenty</MenuItem>
<MenuItem value={30}>Thirty</MenuItem>
</Select>
</FormControl>
);
}
기타 props (Other props)
import * as React from 'react';
import InputLabel from '@mui/material/InputLabel';
import MenuItem from '@mui/material/MenuItem';
import FormHelperText from '@mui/material/FormHelperText';
import FormControl from '@mui/material/FormControl';
import Select, { SelectChangeEvent } from '@mui/material/Select';
export default function SelectOtherProps() {
const [age, setAge] = React.useState('');
const handleChange = (event: SelectChangeEvent) => {
setAge(event.target.value);
};
return (
<div>
<FormControl sx={{ m: 1, minWidth: 120 }} disabled>
<InputLabel id="demo-simple-select-disabled-label">Age</InputLabel>
<Select
labelId="demo-simple-select-disabled-label"
id="demo-simple-select-disabled"
value={age}
label="Age"
onChange={handleChange}
>
<MenuItem value="">
<em>None</em>
</MenuItem>
<MenuItem value={10}>Ten</MenuItem>
<MenuItem value={20}>Twenty</MenuItem>
<MenuItem value={30}>Thirty</MenuItem>
</Select>
<FormHelperText>Disabled</FormHelperText>
</FormControl>
<FormControl sx={{ m: 1, minWidth: 120 }} error>
<InputLabel id="demo-simple-select-error-label">Age</InputLabel>
<Select
labelId="demo-simple-select-error-label"
id="demo-simple-select-error"
value={age}
label="Age"
onChange={handleChange}
renderValue={(value) => `⚠️ - ${value}`}
>
<MenuItem value="">
<em>None</em>
</MenuItem>
<MenuItem value={10}>Ten</MenuItem>
<MenuItem value={20}>Twenty</MenuItem>
<MenuItem value={30}>Thirty</MenuItem>
</Select>
<FormHelperText>Error</FormHelperText>
</FormControl>
<FormControl sx={{ m: 1, minWidth: 120 }}>
<InputLabel id="demo-simple-select-readonly-label">Age</InputLabel>
<Select
labelId="demo-simple-select-readonly-label"
id="demo-simple-select-readonly"
value={age}
label="Age"
onChange={handleChange}
inputProps={{ readOnly: true }}
>
<MenuItem value="">
<em>None</em>
</MenuItem>
<MenuItem value={10}>Ten</MenuItem>
<MenuItem value={20}>Twenty</MenuItem>
<MenuItem value={30}>Thirty</MenuItem>
</Select>
<FormHelperText>Read only</FormHelperText>
</FormControl>
<FormControl required sx={{ m: 1, minWidth: 120 }}>
<InputLabel id="demo-simple-select-required-label">Age</InputLabel>
<Select
labelId="demo-simple-select-required-label"
id="demo-simple-select-required"
value={age}
label="Age *"
onChange={handleChange}
>
<MenuItem value="">
<em>None</em>
</MenuItem>
<MenuItem value={10}>Ten</MenuItem>
<MenuItem value={20}>Twenty</MenuItem>
<MenuItem value={30}>Thirty</MenuItem>
</Select>
<FormHelperText>Required</FormHelperText>
</FormControl>
</div>
);
}
네이티브 select (Native select)
모바일에서는 플랫폼의 네이티브 select를 사용하면 사용자 경험이 더 좋아질 수 있어서, 이 패턴을 허용해요.
import * as React from 'react';
import Box from '@mui/material/Box';
import InputLabel from '@mui/material/InputLabel';
import FormControl from '@mui/material/FormControl';
import NativeSelect from '@mui/material/NativeSelect';
export default function NativeSelectDemo() {
const id = React.useId();
return (
<Box sx={{ minWidth: 120 }}>
<FormControl fullWidth>
<InputLabel variant="standard" htmlFor={`${id}-select`}>
Age
</InputLabel>
<NativeSelect
defaultValue={30}
inputProps={{
name: 'age',
id: `${id}-select`,
}}
>
<option value={10}>Ten</option>
<option value={20}>Twenty</option>
<option value={30}>Thirty</option>
</NativeSelect>
</FormControl>
</Box>
);
}
TextField
TextField 래퍼 컴포넌트는 라벨, 입력, 도움말 텍스트를 포함하는 완전한 폼 컨트롤이에요. select 모드로 사용하는 예시는 이 섹션에서 찾을 수 있어요.
커스터마이즈 (Customization)
컴포넌트를 커스터마이즈하는 몇 가지 예시를 볼게요. 자세한 내용은 overrides 문서 페이지에서 확인할 수 있어요.
첫 단계는 InputBase 컴포넌트를 스타일링하는 거예요. 스타일이 적용되면 텍스트 필드로 직접 사용하거나, select의 input prop에 제공해 select 필드로 만들 수 있어요. "standard" variant는 내용을 fieldset/legend 마크업으로 감싸지 않아서 커스터마이즈하기 더 쉽다는 점에 유의하세요.
import * as React from 'react';
import { styled } from '@mui/material/styles';
import InputLabel from '@mui/material/InputLabel';
import MenuItem from '@mui/material/MenuItem';
import FormControl from '@mui/material/FormControl';
import Select from '@mui/material/Select';
import NativeSelect from '@mui/material/NativeSelect';
import InputBase from '@mui/material/InputBase';
const BootstrapInput = styled(InputBase)(({ theme }) => ({
'label + &': {
marginTop: theme.spacing(3),
},
'& .MuiInputBase-input': {
borderRadius: 4,
position: 'relative',
backgroundColor: (theme.vars ?? theme).palette.background.paper,
border: '1px solid #ced4da',
fontSize: 16,
padding: '10px 26px 10px 12px',
transition: theme.transitions.create(['border-color', 'box-shadow']),
// Use the system font instead of the default Roboto font.
fontFamily: [
'-apple-system',
'BlinkMacSystemFont',
'"Segoe UI"',
'Roboto',
'"Helvetica Neue"',
'Arial',
'sans-serif',
'"Apple Color Emoji"',
'"Segoe UI Emoji"',
'"Segoe UI Symbol"',
].join(','),
'&:focus': {
borderRadius: 4,
borderColor: '#80bdff',
boxShadow: '0 0 0 0.2rem rgba(0,123,255,.25)',
},
},
}));
export default function CustomizedSelects() {
const textboxId = React.useId();
const selectId = React.useId();
const nativeId = React.useId();
const [age, setAge] = React.useState('');
const handleChange = (event: { target: { value: string } }) => {
setAge(event.target.value);
};
return (
<div>
<FormControl sx={{ m: 1 }} variant="standard">
<InputLabel htmlFor={`${textboxId}-input`}>Age</InputLabel>
<BootstrapInput id={`${textboxId}-input`} />
</FormControl>
<FormControl sx={{ m: 1 }} variant="standard">
<InputLabel id={`${selectId}-label`}>Age</InputLabel>
<Select
labelId={`${selectId}-label`}
id={`${selectId}-select`}
value={age}
onChange={handleChange}
input={<BootstrapInput />}
>
<MenuItem value="">
<em>None</em>
</MenuItem>
<MenuItem value={10}>Ten</MenuItem>
<MenuItem value={20}>Twenty</MenuItem>
<MenuItem value={30}>Thirty</MenuItem>
</Select>
</FormControl>
<FormControl sx={{ m: 1 }} variant="standard">
<InputLabel htmlFor={`${nativeId}-select`}>Age</InputLabel>
<NativeSelect
id={`${nativeId}-select`}
value={age}
onChange={handleChange}
input={<BootstrapInput />}
>
<option aria-label="None" value="" />
<option value={10}>Ten</option>
<option value={20}>Twenty</option>
<option value={30}>Thirty</option>
</NativeSelect>
</FormControl>
</div>
);
}
🎨 영감이 필요하다면 MUI Treasury의 커스터마이즈 예시를 확인해 보세요.
다중 select (Multiple select)
Select 컴포넌트는 여러 선택(multiple selection)을 처리할 수 있어요. multiple prop으로 활성화돼요.
단일 선택과 마찬가지로, onChange 콜백에서 event.target.value에 접근해 새 값을 꺼낼 수 있어요. 이 값은 항상 배열이에요.
기본 (Default)
import * as React from 'react';
import { Theme, useTheme } from '@mui/material/styles';
import OutlinedInput from '@mui/material/OutlinedInput';
import InputLabel from '@mui/material/InputLabel';
import MenuItem from '@mui/material/MenuItem';
import FormControl from '@mui/material/FormControl';
import Select, { SelectChangeEvent } from '@mui/material/Select';
const ITEM_HEIGHT = 48;
const ITEM_PADDING_TOP = 8;
const MenuProps = {
slotProps: {
paper: {
style: {
maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
width: 250,
},
},
},
};
const names = [
'Oliver Hansen',
'Van Henry',
'April Tucker',
'Ralph Hubbard',
'Omar Alexander',
'Carlos Abbott',
'Miriam Wagner',
'Bradley Wilkerson',
'Virginia Andrews',
'Kelly Snyder',
];
function getStyles(name: string, personName: string[], theme: Theme) {
return {
fontWeight: personName.includes(name)
? theme.typography.fontWeightMedium
: theme.typography.fontWeightRegular,
};
}
export default function MultipleSelect() {
const theme = useTheme();
const [personName, setPersonName] = React.useState<string[]>([]);
const handleChange = (event: SelectChangeEvent<typeof personName>) => {
const {
target: { value },
} = event;
setPersonName(
// On autofill we get a stringified value.
typeof value === 'string' ? value.split(',') : value,
);
};
return (
<div>
<FormControl sx={{ m: 1, width: 300 }}>
<InputLabel id="demo-multiple-name-label">Name</InputLabel>
<Select
labelId="demo-multiple-name-label"
id="demo-multiple-name"
multiple
value={personName}
onChange={handleChange}
input={<OutlinedInput label="Name" />}
MenuProps={MenuProps}
>
{names.map((name) => (
<MenuItem
key={name}
value={name}
style={getStyles(name, personName, theme)}
>
{name}
</MenuItem>
))}
</Select>
</FormControl>
</div>
);
}
선택 표시 (Selection indicators)
이 예시는 listbox의 각 항목 선택 상태를 나타내기 위해 아이콘을 사용하는 방법을 보여줘요.
import * as React from 'react';
import OutlinedInput from '@mui/material/OutlinedInput';
import InputLabel from '@mui/material/InputLabel';
import MenuItem from '@mui/material/MenuItem';
import FormControl from '@mui/material/FormControl';
import ListItemText from '@mui/material/ListItemText';
import Select, { SelectChangeEvent } from '@mui/material/Select';
import CheckBoxOutlineBlankIcon from '@mui/icons-material/CheckBoxOutlineBlank';
import CheckBoxIcon from '@mui/icons-material/CheckBox';
const ITEM_HEIGHT = 48;
const ITEM_PADDING_TOP = 8;
const MenuProps = {
slotProps: {
paper: {
style: {
maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
width: 250,
},
},
},
};
const names = [
'Oliver Hansen',
'Van Henry',
'April Tucker',
'Ralph Hubbard',
'Omar Alexander',
'Carlos Abbott',
'Miriam Wagner',
'Bradley Wilkerson',
'Virginia Andrews',
'Kelly Snyder',
];
export default function MultipleSelectCheckmarks() {
const [personName, setPersonName] = React.useState<string[]>([]);
const handleChange = (event: SelectChangeEvent<typeof personName>) => {
const {
target: { value },
} = event;
setPersonName(
// On autofill we get a stringified value.
typeof value === 'string' ? value.split(',') : value,
);
};
return (
<div>
<FormControl sx={{ m: 1, width: 300 }}>
<InputLabel id="demo-multiple-checkbox-label">Tag</InputLabel>
<Select
labelId="demo-multiple-checkbox-label"
id="demo-multiple-checkbox"
multiple
value={personName}
onChange={handleChange}
input={<OutlinedInput label="Tag" />}
renderValue={(selected) => selected.join(', ')}
MenuProps={MenuProps}
>
{names.map((name) => {
const selected = personName.includes(name);
const SelectionIcon = selected ? CheckBoxIcon : CheckBoxOutlineBlankIcon;
return (
<MenuItem key={name} value={name}>
<SelectionIcon
fontSize="small"
style={{ marginRight: 8, padding: 9, boxSizing: 'content-box' }}
/>
<ListItemText primary={name} />
</MenuItem>
);
})}
</Select>
</FormControl>
</div>
);
}
칩 (Chip)
import * as React from 'react';
import { Theme, useTheme } from '@mui/material/styles';
import Box from '@mui/material/Box';
import OutlinedInput from '@mui/material/OutlinedInput';
import InputLabel from '@mui/material/InputLabel';
import MenuItem from '@mui/material/MenuItem';
import FormControl from '@mui/material/FormControl';
import Select, { SelectChangeEvent } from '@mui/material/Select';
import Chip from '@mui/material/Chip';
const ITEM_HEIGHT = 48;
const ITEM_PADDING_TOP = 8;
const MenuProps = {
slotProps: {
paper: {
style: {
maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
width: 250,
},
},
},
};
const names = [
'Oliver Hansen',
'Van Henry',
'April Tucker',
'Ralph Hubbard',
'Omar Alexander',
'Carlos Abbott',
'Miriam Wagner',
'Bradley Wilkerson',
'Virginia Andrews',
'Kelly Snyder',
];
function getStyles(name: string, personName: readonly string[], theme: Theme) {
return {
fontWeight: personName.includes(name)
? theme.typography.fontWeightMedium
: theme.typography.fontWeightRegular,
};
}
export default function MultipleSelectChip() {
const theme = useTheme();
const [personName, setPersonName] = React.useState<string[]>([]);
const handleChange = (event: SelectChangeEvent<typeof personName>) => {
const {
target: { value },
} = event;
setPersonName(
// On autofill we get a stringified value.
typeof value === 'string' ? value.split(',') : value,
);
};
return (
<div>
<FormControl sx={{ m: 1, width: 300 }}>
<InputLabel id="demo-multiple-chip-label">Chip</InputLabel>
<Select
labelId="demo-multiple-chip-label"
id="demo-multiple-chip"
multiple
value={personName}
onChange={handleChange}
input={<OutlinedInput id="select-multiple-chip" label="Chip" />}
renderValue={(selected) => (
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 0.5 }}>
{selected.map((value) => (
<Chip key={value} label={value} />
))}
</Box>
)}
MenuProps={MenuProps}
>
{names.map((name) => (
<MenuItem
key={name}
value={name}
style={getStyles(name, personName, theme)}
>
{name}
</MenuItem>
))}
</Select>
</FormControl>
</div>
);
}
플레이스홀더 (Placeholder)
import * as React from 'react';
import { Theme, useTheme } from '@mui/material/styles';
import OutlinedInput from '@mui/material/OutlinedInput';
import MenuItem from '@mui/material/MenuItem';
import FormControl from '@mui/material/FormControl';
import Select, { SelectChangeEvent } from '@mui/material/Select';
const ITEM_HEIGHT = 48;
const ITEM_PADDING_TOP = 8;
const MenuProps = {
slotProps: {
paper: {
style: {
maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
width: 250,
},
},
},
};
const names = [
'Oliver Hansen',
'Van Henry',
'April Tucker',
'Ralph Hubbard',
'Omar Alexander',
'Carlos Abbott',
'Miriam Wagner',
'Bradley Wilkerson',
'Virginia Andrews',
'Kelly Snyder',
];
function getStyles(name: string, personName: readonly string[], theme: Theme) {
return {
fontWeight: personName.includes(name)
? theme.typography.fontWeightMedium
: theme.typography.fontWeightRegular,
};
}
export default function MultipleSelectPlaceholder() {
const theme = useTheme();
const [personName, setPersonName] = React.useState<string[]>([]);
const handleChange = (event: SelectChangeEvent<typeof personName>) => {
const {
target: { value },
} = event;
setPersonName(
// On autofill we get a stringified value.
typeof value === 'string' ? value.split(',') : value,
);
};
return (
<div>
<FormControl sx={{ m: 1, width: 300, mt: 3 }}>
<Select
multiple
displayEmpty
value={personName}
onChange={handleChange}
input={<OutlinedInput />}
renderValue={(selected) => {
if (selected.length === 0) {
return <em>Placeholder</em>;
}
return selected.join(', ');
}}
MenuProps={MenuProps}
inputProps={{ 'aria-label': 'Without label' }}
>
<MenuItem disabled value="">
<em>Placeholder</em>
</MenuItem>
{names.map((name) => (
<MenuItem
key={name}
value={name}
style={getStyles(name, personName, theme)}
>
{name}
</MenuItem>
))}
</Select>
</FormControl>
</div>
);
}
네이티브 (Native)
import * as React from 'react';
import InputLabel from '@mui/material/InputLabel';
import FormControl from '@mui/material/FormControl';
import Select from '@mui/material/Select';
const names = [
'Oliver Hansen',
'Van Henry',
'April Tucker',
'Ralph Hubbard',
'Omar Alexander',
'Carlos Abbott',
'Miriam Wagner',
'Bradley Wilkerson',
'Virginia Andrews',
'Kelly Snyder',
];
export default function MultipleSelectNative() {
const id = React.useId();
const [personName, setPersonName] = React.useState<string[]>([]);
const handleChangeMultiple = (event: React.ChangeEvent<HTMLSelectElement>) => {
const { options } = event.target;
const value: string[] = [];
for (let i = 0, l = options.length; i < l; i += 1) {
if (options[i].selected) {
value.push(options[i].value);
}
}
setPersonName(value);
};
return (
<div>
<FormControl sx={{ m: 1, minWidth: 120, maxWidth: 300 }}>
<InputLabel shrink htmlFor={`${id}-select`}>
Native
</InputLabel>
<Select<string[]>
multiple
native
value={personName}
// @ts-ignore Typings are not considering `native`
onChange={handleChangeMultiple}
label="Native"
inputProps={{
id: `${id}-select`,
}}
>
{names.map((name) => (
<option key={name} value={name}>
{name}
</option>
))}
</Select>
</FormControl>
</div>
);
}
열린 상태 제어하기 (Controlling the open state)
open prop으로 select의 열린 상태를 제어할 수 있어요. 또는 defaultOpen prop으로 컴포넌트의 초기(비제어·uncontrolled) 열린 상태를 설정할 수도 있어요.
:::info
- 컴포넌트가 부모에 의해 props를 통해 관리되면 **제어(controlled)**됩니다.
- 컴포넌트가 자체 로컬 상태로 관리되면 **비제어(uncontrolled)**됩니다.
제어(controlled)와 비제어 컴포넌트에 대해 더 알아보려면 React 문서를 참고하세요. :::
import * as React from 'react';
import InputLabel from '@mui/material/InputLabel';
import MenuItem from '@mui/material/MenuItem';
import FormControl from '@mui/material/FormControl';
import Select, { SelectChangeEvent } from '@mui/material/Select';
import Button from '@mui/material/Button';
export default function ControlledOpenSelect() {
const [age, setAge] = React.useState<string | number>('');
const [open, setOpen] = React.useState(false);
const handleChange = (event: SelectChangeEvent<typeof age>) => {
setAge(event.target.value);
};
const handleClose = () => {
setOpen(false);
};
const handleOpen = () => {
setOpen(true);
};
return (
<div>
<Button sx={{ display: 'block', mt: 2 }} onClick={handleOpen}>
Open the select
</Button>
<FormControl sx={{ m: 1, minWidth: 120 }}>
<InputLabel id="demo-controlled-open-select-label">Age</InputLabel>
<Select
labelId="demo-controlled-open-select-label"
id="demo-controlled-open-select"
open={open}
onClose={handleClose}
onOpen={handleOpen}
value={age}
label="Age"
onChange={handleChange}
>
<MenuItem value="">
<em>None</em>
</MenuItem>
<MenuItem value={10}>Ten</MenuItem>
<MenuItem value={20}>Twenty</MenuItem>
<MenuItem value={30}>Thirty</MenuItem>
</Select>
</FormControl>
</div>
);
}
다이얼로그와 함께 (With a dialog)
Material Design 가이드라인에서는 권장되지 않지만, 다이얼로그 안에서 select를 사용할 수 있어요.
import * as React from 'react';
import Box from '@mui/material/Box';
import Button from '@mui/material/Button';
import Dialog from '@mui/material/Dialog';
import DialogActions from '@mui/material/DialogActions';
import DialogContent from '@mui/material/DialogContent';
import DialogTitle from '@mui/material/DialogTitle';
import InputLabel from '@mui/material/InputLabel';
import OutlinedInput from '@mui/material/OutlinedInput';
import MenuItem from '@mui/material/MenuItem';
import FormControl from '@mui/material/FormControl';
import Select, { SelectChangeEvent } from '@mui/material/Select';
export default function DialogSelect() {
const nativeId = React.useId();
const selectId = React.useId();
const [open, setOpen] = React.useState(false);
const [age, setAge] = React.useState<number | string>('');
const handleChange = (event: SelectChangeEvent<typeof age>) => {
setAge(Number(event.target.value) || '');
};
const handleClickOpen = () => {
setOpen(true);
};
const handleDialogClose = (
_event: React.SyntheticEvent<unknown>,
reason: string,
) => {
if (!['backdropClick', 'escapeKeyDown'].includes(reason)) {
setOpen(false);
}
};
const handleActionButtonClick = () => {
setOpen(false);
};
return (
<div>
<Button onClick={handleClickOpen}>Open select dialog</Button>
<Dialog open={open} onClose={handleDialogClose}>
<DialogTitle>Fill the form</DialogTitle>
<DialogContent>
<Box component="form" sx={{ display: 'flex', flexWrap: 'wrap' }}>
<FormControl sx={{ m: 1, minWidth: 120 }}>
<InputLabel htmlFor={`${nativeId}-select`}>Age</InputLabel>
<Select
native
value={age}
onChange={handleChange}
input={<OutlinedInput label="Age" id={`${nativeId}-select`} />}
>
<option aria-label="None" value="" />
<option value={10}>Ten</option>
<option value={20}>Twenty</option>
<option value={30}>Thirty</option>
</Select>
</FormControl>
<FormControl sx={{ m: 1, minWidth: 120 }}>
<InputLabel id={`${selectId}-label`}>Age</InputLabel>
<Select
labelId={`${selectId}-label`}
id={`${selectId}-select`}
value={age}
onChange={handleChange}
input={<OutlinedInput label="Age" />}
>
<MenuItem value="">
<em>None</em>
</MenuItem>
<MenuItem value={10}>Ten</MenuItem>
<MenuItem value={20}>Twenty</MenuItem>
<MenuItem value={30}>Thirty</MenuItem>
</Select>
</FormControl>
</Box>
</DialogContent>
<DialogActions>
<Button onClick={handleActionButtonClick}>Cancel</Button>
<Button onClick={handleActionButtonClick}>Ok</Button>
</DialogActions>
</Dialog>
</div>
);
}
그룹화 (Grouping)
ListSubheader 컴포넌트나 네이티브 <optgroup> 요소로 카테고리를 표시해요.
import * as React from 'react';
import InputLabel from '@mui/material/InputLabel';
import MenuItem from '@mui/material/MenuItem';
import ListSubheader from '@mui/material/ListSubheader';
import FormControl from '@mui/material/FormControl';
import Select from '@mui/material/Select';
export default function GroupedSelect() {
const nativeId = React.useId();
const id = React.useId();
return (
<div>
<FormControl sx={{ m: 1, minWidth: 120 }}>
<InputLabel htmlFor={`${nativeId}-select`}>Grouping</InputLabel>
<Select native defaultValue="" id={`${nativeId}-select`} label="Grouping">
<option aria-label="None" value="" />
<optgroup label="Category 1">
<option value={1}>Option 1</option>
<option value={2}>Option 2</option>
</optgroup>
<optgroup label="Category 2">
<option value={3}>Option 3</option>
<option value={4}>Option 4</option>
</optgroup>
</Select>
</FormControl>
<FormControl sx={{ m: 1, minWidth: 120 }}>
<InputLabel id={`${id}-label`}>Grouping</InputLabel>
<Select
defaultValue=""
id={`${id}-select`}
label="Grouping"
SelectDisplayProps={{
'aria-labelledby': `${id}-label`,
}}
>
<MenuItem value="">
<em>None</em>
</MenuItem>
<ListSubheader>Category 1</ListSubheader>
<MenuItem value={1}>Option 1</MenuItem>
<MenuItem value={2}>Option 2</MenuItem>
<ListSubheader>Category 2</ListSubheader>
<MenuItem value={3}>Option 3</MenuItem>
<MenuItem value={4}>Option 4</MenuItem>
</Select>
</FormControl>
</div>
);
}
접근성 (Accessibility)
Select 입력에 올바르게 라벨을 지정하려면 라벨을 담은 id를 가진 추가 요소가 필요해요. 그 id는 Select의 labelId와 일치해야 해요, 예:
<InputLabel id="label">Age</InputLabel>
<Select labelId="label" id="select" value="20">
<MenuItem value="10">Ten</MenuItem>
<MenuItem value="20">Twenty</MenuItem>
</Select>
또는 id와 label을 가진 TextField가 적절한 마크업과 id를 만들어 주기도 해요:
<TextField id="select" label="Age" value="20" select>
<MenuItem value="10">Ten</MenuItem>
<MenuItem value="20">Twenty</MenuItem>
</TextField>
네이티브 select의 경우, select 요소의 id 속성 값을 InputLabel의 htmlFor 속성에 주어 라벨을 언급해야 해요:
<InputLabel htmlFor="select">Age</InputLabel>
<NativeSelect id="select">
<option value="10">Ten</option>
<option value="20">Twenty</option>
</NativeSelect>
NativeSelect API
Demos
이 React 컴포넌트 사용법에 대한 예시와 자세한 내용은 컴포넌트 데모 페이지를 방문해 주세요:
Import
import NativeSelect from '@mui/material/NativeSelect';
// or
import { NativeSelect } from '@mui/material';
Props
| Name | Type | Default | Required | Description |
|---|---|---|---|---|
| children | node |
- | No | 컴포넌트 콘텐츠입니다. |
| classes | object |
{} |
No | 컴포넌트에 적용되는 스타일을 덮어쓰거나 확장합니다. |
| IconComponent | elementType |
ArrowDropDownIcon |
No | 아이콘 컴포넌트입니다. |
| input | element |
<Input /> |
No | <Input /> 요소입니다. |
| inputProps | object |
- | No | input 요소에 전달할 속성입니다. |
| onChange | function(event: React.ChangeEvent<HTMLSelectElement>) => void |
- | No | 네이티브 select의 값이 바뀌었을 때 호출되는 콜백입니다. |
| sx | Array<func | object | bool> | func | object |
- | No | 시스템 오버라이드와 추가 CSS 스타일을 정의할 수 있게 해주는 시스템 prop입니다. |
| value | any |
- | No | select의 값입니다. |
| variant | 'filled' | 'outlined' | 'standard' |
- | No | 컴포넌트의 변형(variant)입니다. |
Note:
ref는 루트 요소(HTMLDivElement)로 전달됩니다.
그 외에 제공된 다른 props는 모두 루트 요소(Input)에 전달됩니다.
상속 (Inheritance)
위에서 명시적으로 문서화되지는 않았지만, Input 컴포넌트의 props도 NativeSelect에서 사용할 수 있어요.
테마 기본 props (Theme default props)
MuiNativeSelect를 사용해 테마로 이 컴포넌트의 기본 props를 변경할 수 있어요.
CSS
규칙 이름 (Rule name)
| Global class | Rule name | Description |
|---|---|---|
.Mui-disabled |
- | select 컴포넌트의 disabled 클래스에 적용되는 상태 클래스입니다. |
.Mui-error |
- | select 컴포넌트의 error 클래스에 적용되는 상태 클래스입니다. |
| - | filled | variant="filled"일 때 select 컴포넌트에 적용되는 스타일입니다. |
| - | icon | 아이콘 컴포넌트에 적용되는 스타일입니다. |
| - | iconFilled | variant="filled"일 때 아이콘 컴포넌트에 적용되는 스타일입니다. |
| - | iconOpen | 팝업이 열려 있을 때 아이콘 컴포넌트에 적용되는 스타일입니다. |
| - | iconOutlined | variant="outlined"일 때 아이콘 컴포넌트에 적용되는 스타일입니다. |
| - | iconStandard | variant="standard"일 때 아이콘 컴포넌트에 적용되는 스타일입니다. |
| - | multiple | multiple={true}일 때 select 컴포넌트에 적용되는 스타일입니다. |
| - | nativeInput | 기저 네이티브 입력 컴포넌트에 적용되는 스타일입니다. |
| - | outlined | variant="outlined"일 때 select 컴포넌트에 적용되는 스타일입니다. |
| - | root | 루트 요소에 적용되는 스타일입니다. |
| - | select | select 컴포넌트의 select 클래스에 적용되는 스타일입니다. |
| - | standard | variant="standard"일 때 select 컴포넌트에 적용되는 스타일입니다. |
소스 코드 (Source code)
이 페이지에서 정보를 찾지 못했다면 컴포넌트 구현을 살펴보는 것도 방법이에요.
Select API
Demos
이 React 컴포넌트 사용법에 대한 예시와 자세한 내용은 컴포넌트 데모 페이지를 방문해 주세요:
Import
import Select from '@mui/material/Select';
// or
import { Select } from '@mui/material';
Props
| Name | Type | Default | Required | Description |
|---|---|---|---|---|
| autoWidth | bool |
false |
No | 입력에 따라 select의 너비를 자동으로 조정할지 여부입니다. |
| children | node |
- | No | 컴포넌트 콘텐츠입니다. |
| classes | object |
{} |
No | 컴포넌트에 적용되는 스타일을 덮어쓰거나 확장합니다. |
| defaultOpen | bool |
false |
No | 초기(비제어) 열린 상태입니다. |
| defaultValue | any |
- | No | 초기(비제어) 값입니다. |
| displayEmpty | bool |
false |
No | 값이 빈 문자열일 때 선택된 값을 표시할지 여부입니다. |
| IconComponent | elementType |
ArrowDropDownIcon |
No | 아이콘 컴포넌트입니다. |
| id | string |
- | No | select의 id입니다. |
| input | element |
- | No | <Input /> 요소입니다. |
| inputProps | object |
- | No | input 요소에 전달할 속성입니다. |
| label | node |
- | No | select의 라벨입니다. |
| labelId | string |
- | No | 라벨 요소의 id입니다. |
| MenuProps | object |
- | No | Menu 컴포넌트에 전달할 속성입니다. |
| multiple | bool |
false |
No | 여러 값(multiple) 선택을 허용할지 여부입니다. |
| native | bool |
false |
No | 네이티브 select을 사용할지 여부입니다. |
| onChange | function(event: SelectChangeEvent<Value>, child?: object) => void |
- | No | 값이 바뀌었을 때 호출되는 콜백입니다. |
| onClose | function(event: object) => void |
- | No | 드롭다운이 닫힐 때 호출되는 콜백입니다. |
| onOpen | function(event: object) => void |
- | No | 드롭다운이 열릴 때 호출되는 콜백입니다. |
| open | bool |
- | No | 드롭다운의 열림 상태를 제어합니다. |
| renderValue | function(value: any) => ReactNode |
- | No | 선택된 값을 렌더링하는 함수입니다. |
| SelectDisplayProps | object |
- | No | select의 표시 요소에 전달할 속성입니다. |
| sx | Array<func | object | bool> | func | object |
- | No | 시스템 오버라이드와 추가 CSS 스타일을 정의할 수 있게 해주는 시스템 prop입니다. |
| value | '' | any |
- | No | select의 값입니다. |
| variant | 'filled' | 'outlined' | 'standard' |
'outlined' |
No | 컴포넌트의 변형(variant)입니다. |
Note:
ref는 루트 요소(HTMLDivElement)로 전달됩니다.
그 외에 제공된 다른 props는 모두 루트 요소(OutlinedInput)에 전달됩니다.
상속 (Inheritance)
위에서 명시적으로 문서화되지는 않았지만, OutlinedInput 컴포넌트의 props도 Select에서 사용할 수 있어요.
테마 기본 props (Theme default props)
MuiSelect를 사용해 테마로 이 컴포넌트의 기본 props를 변경할 수 있어요.
CSS
규칙 이름 (Rule name)
| Global class | Rule name | Description |
|---|---|---|
.Mui-disabled |
- | select 컴포넌트의 disabled 클래스에 적용되는 상태 클래스입니다. |
.Mui-error |
- | error={true}일 때 루트 요소에 적용되는 상태 클래스입니다. |
| - | filled | variant="filled"일 때 select 컴포넌트에 적용되는 스타일입니다. |
.Mui-focused |
- | select가 포커스되었을 때 select 컴포넌트에 적용되는 스타일입니다. |
| - | icon | 아이콘 컴포넌트에 적용되는 스타일입니다. |
| - | iconOpen | 팝업이 열려 있을 때 아이콘 컴포넌트에 적용되는 스타일입니다. |
| - | multiple | multiple={true}일 때 select 컴포넌트에 적용되는 스타일입니다. |
| - | nativeInput | 기저 네이티브 입력 컴포넌트에 적용되는 스타일입니다. |
| - | outlined | variant="outlined"일 때 select 컴포넌트에 적용되는 스타일입니다. |
| - | root | 루트 요소에 적용되는 스타일입니다. |
| - | select | select 컴포넌트의 select 클래스에 적용되는 스타일입니다. |
| - | standard | variant="standard"일 때 select 컴포넌트에 적용되는 스타일입니다. |
소스 코드 (Source code)
이 페이지에서 정보를 찾지 못했다면 컴포넌트 구현을 살펴보는 것도 방법이에요.
더 알아보기 (Learn more)
- Autocomplete — combobox, 다중 선택, 자동 완성 등 고급 기능
- TextField — 라벨·입력·도움말 텍스트를 포함한 완전한 폼 컨트롤
- InputBase · OutlinedInput — Select의 기저 입력 컴포넌트
- Material Design — Select · WAI-ARIA Combobox 패턴