Switch

Switch (스위치)

단일 설정의 켜짐/꺼짐 상태를 토글하는 스위치 컴포넌트를 알아볼게요. 특히 모바일에서 설정을 조정할 때 선호되는 선택 컨트롤이라서, 상태를 명확하게 보여주는 데 유용해요.

출처: 문서

본문

스위치는 단일 설정의 상태를 켜짐(on) 또는 꺼짐(off)으로 토글해요.

스위치는 모바일에서 설정을 조정하는 데 선호되는 방식이에요. 스위치가 제어하는 옵션과 그것이 어떤 상태인지는 해당 인라인 레이블에서 분명하게 드러나야 해요.

기본 스위치 (Basic switches)

import Switch from '@mui/material/Switch';

const label = { slotProps: { input: { 'aria-label': 'Switch demo' } } };

export default function BasicSwitches() {
  return (
    <div>
      <Switch {...label} defaultChecked />
      <Switch {...label} />
      <Switch {...label} disabled defaultChecked />
      <Switch {...label} disabled />
    </div>
  );
}

레이블 (Label)

FormControlLabel 컴포넌트 덕분에 Switch에 레이블을 제공할 수 있어요.

import FormGroup from '@mui/material/FormGroup';
import FormControlLabel from '@mui/material/FormControlLabel';
import Switch from '@mui/material/Switch';

export default function SwitchLabels() {
  return (
    <FormGroup>
      <FormControlLabel control={<Switch defaultChecked />} label="Label" />
      <FormControlLabel required control={<Switch />} label="Required" />
      <FormControlLabel disabled control={<Switch />} label="Disabled" />
    </FormGroup>
  );
}

크기 (Size)

size prop을 사용해서 스위치의 크기를 변경해요.

import Switch from '@mui/material/Switch';

const label = { slotProps: { input: { 'aria-label': 'Size switch demo' } } };

export default function SwitchesSize() {
  return (
    <div>
      <Switch {...label} defaultChecked size="small" />
      <Switch {...label} defaultChecked />
    </div>
  );
}

색상 (Color)

import { alpha, styled } from '@mui/material/styles';
import { pink } from '@mui/material/colors';
import Switch from '@mui/material/Switch';

const PinkSwitch = styled(Switch)(({ theme }) => ({
  '& .MuiSwitch-switchBase.Mui-checked': {
    color: pink[600],
    '&:hover': {
      backgroundColor: alpha(pink[600], theme.palette.action.hoverOpacity),
    },
  },
  '& .MuiSwitch-switchBase.Mui-checked + .MuiSwitch-track': {
    backgroundColor: pink[600],
  },
}));

const label = { slotProps: { input: { 'aria-label': 'Color switch demo' } } };

export default function ColorSwitches() {
  return (
    <div>
      <Switch {...label} defaultChecked />
      <Switch {...label} defaultChecked color="secondary" />
      <Switch {...label} defaultChecked color="warning" />
      <Switch {...label} defaultChecked color="default" />
      <PinkSwitch {...label} defaultChecked />
    </div>
  );
}

제어됨 (Controlled)

checked와 onChange prop으로 스위치를 제어할 수 있어요:

import * as React from 'react';
import Switch from '@mui/material/Switch';

export default function ControlledSwitches() {
  const [checked, setChecked] = React.useState(true);

  const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    setChecked(event.target.checked);
  };

  return (
    <Switch
      checked={checked}
      onChange={handleChange}
      slotProps={{ input: { 'aria-label': 'controlled' } }}
    />
  );
}

FormGroup과 함께 쓰는 스위치 (Switches with FormGroup)

FormGroup은 선택 컨트롤 컴포넌트를 그룹화하는 데 사용되는 유용한 래퍼로, 더 쉬운 API를 제공해요. 하지만 관련된 여러 컨트롤이 필요하다면 Checkboxes를 사용하는 것이 좋아요. (참고: When to use).

import * as React from 'react';
import FormLabel from '@mui/material/FormLabel';
import FormControl from '@mui/material/FormControl';
import FormGroup from '@mui/material/FormGroup';
import FormControlLabel from '@mui/material/FormControlLabel';
import FormHelperText from '@mui/material/FormHelperText';
import Switch from '@mui/material/Switch';

export default function SwitchesGroup() {
  const [state, setState] = React.useState({
    gilad: true,
    jason: false,
    antoine: true,
  });

  const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    setState({
      ...state,
      [event.target.name]: event.target.checked,
    });
  };

  return (
    <FormControl component="fieldset" variant="standard">
      <FormLabel component="legend">Assign responsibility</FormLabel>
      <FormGroup>
        <FormControlLabel
          control={
            <Switch checked={state.gilad} onChange={handleChange} name="gilad" />
          }
          label="Gilad Gray"
        />
        <FormControlLabel
          control={
            <Switch checked={state.jason} onChange={handleChange} name="jason" />
          }
          label="Jason Killian"
        />
        <FormControlLabel
          control={
            <Switch checked={state.antoine} onChange={handleChange} name="antoine" />
          }
          label="Antoine Llorca"
        />
      </FormGroup>
      <FormHelperText>Be careful</FormHelperText>
    </FormControl>
  );
}

커스터마이즈 (Customization)

컴포넌트를 커스터마이즈하는 몇 가지 예제예요. 이에 대해 더 자세히 알아보려면 overrides 문서 페이지를 참고하세요.

import { styled } from '@mui/material/styles';
import FormGroup from '@mui/material/FormGroup';
import FormControlLabel from '@mui/material/FormControlLabel';
import Switch, { SwitchProps } from '@mui/material/Switch';
import Stack from '@mui/material/Stack';
import Typography from '@mui/material/Typography';

const MaterialUISwitch = styled(Switch)(({ theme }) => ({
  width: 62,
  height: 34,
  padding: 7,
  '& .MuiSwitch-switchBase': {
    margin: 1,
    padding: 0,
    transform: 'translateX(6px)',
    '&.Mui-checked': {
      color: '#fff',
      transform: 'translateX(22px)',
      '& .MuiSwitch-thumb:before': {
        backgroundImage: `url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" height="20" width="20" viewBox="0 0 20 20"><path fill="${encodeURIComponent(
          '#fff',
        )}" d="M4.2 2.5l-.7 1.8-1.8.7 1.8.7.7 1.8.6-1.8L6.7 5l-1.9-.7-.6-1.8zm15 8.3a6.7 6.7 0 11-6.6-6.6 5.8 5.8 0 006.6 6.6z"/></svg>')`,
      },
      '& + .MuiSwitch-track': {
        opacity: 1,
        backgroundColor: '#aab4be',
        ...theme.applyStyles('dark', {
          backgroundColor: '#8796A5',
        }),
      },
    },
  },
  '& .MuiSwitch-thumb': {
    backgroundColor: '#001e3c',
    width: 32,
    height: 32,
    '&::before': {
      content: "''",
      position: 'absolute',
      width: '100%',
      height: '100%',
      left: 0,
      top: 0,
      backgroundRepeat: 'no-repeat',
      backgroundPosition: 'center',
      backgroundImage: `url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" height="20" width="20" viewBox="0 0 20 20"><path fill="${encodeURIComponent(
        '#fff',
      )}" d="M9.305 1.667V3.75h1.389V1.667h-1.39zm-4.707 1.95l-.982.982L5.09 6.072l.982-.982-1.473-1.473zm10.802 0L13.927 5.09l.982.982 1.473-1.473-.982-.982zM10 5.139a4.872 4.872 0 00-4.862 4.86A4.872 4.872 0 0010 14.862 4.872 4.872 0 0014.86 10 4.872 4.872 0 0010 5.139zm0 1.389A3.462 3.462 0 0113.471 10a3.462 3.462 0 01-3.473 3.472A3.462 3.462 0 016.527 10 3.462 3.462 0 0110 6.528zM1.665 9.305v1.39h2.083v-1.39H1.666zm14.583 0v1.39h2.084v-1.39h-2.084zM5.09 13.928L3.616 15.4l.982.982 1.473-1.473-.982-.982zm9.82 0l-.982.982 1.473 1.473.982-.982-1.473-1.473zM9.305 16.25v2.083h1.389V16.25h-1.39z"/></svg>')`,
    },
    ...theme.applyStyles('dark', {
      backgroundColor: '#003892',
    }),
  },
  '& .MuiSwitch-track': {
    opacity: 1,
    backgroundColor: '#aab4be',
    borderRadius: 20 / 2,
    ...theme.applyStyles('dark', {
      backgroundColor: '#8796A5',
    }),
  },
}));

const Android12Switch = styled(Switch)(({ theme }) => ({
  padding: 8,
  '& .MuiSwitch-track': {
    borderRadius: 22 / 2,
    '&::before, &::after': {
      content: '""',
      position: 'absolute',
      top: '50%',
      transform: 'translateY(-50%)',
      width: 16,
      height: 16,
    },
    '&::before': {
      backgroundImage: `url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16" viewBox="0 0 24 24"><path fill="${encodeURIComponent(
        theme.palette.getContrastText(theme.palette.primary.main),
      )}" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"/></svg>')`,
      left: 12,
    },
    '&::after': {
      backgroundImage: `url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16" viewBox="0 0 24 24"><path fill="${encodeURIComponent(
        theme.palette.getContrastText(theme.palette.primary.main),
      )}" d="M19,13H5V11H19V13Z" /></svg>')`,
      right: 12,
    },
  },
  '& .MuiSwitch-thumb': {
    boxShadow: 'none',
    width: 16,
    height: 16,
    margin: 2,
  },
}));

const IOSSwitch = styled((props: SwitchProps) => (
  <Switch focusVisibleClassName=".Mui-focusVisible" disableRipple {...props} />
))(({ theme }) => ({
  width: 42,
  height: 26,
  padding: 0,
  '& .MuiSwitch-switchBase': {
    padding: 0,
    margin: 2,
    transitionDuration: '300ms',
    '&.Mui-checked': {
      transform: 'translateX(16px)',
      color: '#fff',
      '& + .MuiSwitch-track': {
        backgroundColor: '#65C466',
        opacity: 1,
        border: 0,
        ...theme.applyStyles('dark', {
          backgroundColor: '#2ECA45',
        }),
      },
      '&.Mui-disabled + .MuiSwitch-track': {
        opacity: 0.5,
      },
    },
    '&.Mui-focusVisible .MuiSwitch-thumb': {
      color: '#33cf4d',
      border: '6px solid #fff',
    },
    '&.Mui-disabled .MuiSwitch-thumb': {
      color: theme.palette.grey[100],
      ...theme.applyStyles('dark', {
        color: theme.palette.grey[600],
      }),
    },
    '&.Mui-disabled + .MuiSwitch-track': {
      opacity: 0.7,
      ...theme.applyStyles('dark', {
        opacity: 0.3,
      }),
    },
  },
  '& .MuiSwitch-thumb': {
    boxSizing: 'border-box',
    width: 22,
    height: 22,
  },
  '& .MuiSwitch-track': {
    borderRadius: 26 / 2,
    backgroundColor: '#E9E9EA',
    opacity: 1,
    transition: theme.transitions.create(['background-color'], {
      duration: 500,
    }),
    ...theme.applyStyles('dark', {
      backgroundColor: '#39393D',
    }),
  },
}));

const AntSwitch = styled(Switch)(({ theme }) => ({
  width: 28,
  height: 16,
  padding: 0,
  display: 'flex',
  '&:active': {
    '& .MuiSwitch-thumb': {
      width: 15,
    },
    '& .MuiSwitch-switchBase.Mui-checked': {
      transform: 'translateX(9px)',
    },
  },
  '& .MuiSwitch-switchBase': {
    padding: 2,
    '&.Mui-checked': {
      transform: 'translateX(12px)',
      color: '#fff',
      '& + .MuiSwitch-track': {
        opacity: 1,
        backgroundColor: '#1890ff',
        ...theme.applyStyles('dark', {
          backgroundColor: '#177ddc',
        }),
      },
    },
  },
  '& .MuiSwitch-thumb': {
    boxShadow: '0 2px 4px 0 rgb(0 35 11 / 20%)',
    width: 12,
    height: 12,
    borderRadius: 6,
    transition: theme.transitions.create(['width'], {
      duration: 200,
    }),
  },
  '& .MuiSwitch-track': {
    borderRadius: 16 / 2,
    opacity: 1,
    backgroundColor: 'rgba(0,0,0,.25)',
    boxSizing: 'border-box',
    ...theme.applyStyles('dark', {
      backgroundColor: 'rgba(255,255,255,.35)',
    }),
  },
}));

export default function CustomizedSwitches() {
  return (
    <FormGroup>
      <FormControlLabel
        control={<MaterialUISwitch sx={{ m: 1 }} defaultChecked />}
        label="MUI switch"
      />
      <FormControlLabel
        control={<Android12Switch defaultChecked />}
        label="Android 12"
      />
      <FormControlLabel
        control={<IOSSwitch sx={{ m: 1 }} defaultChecked />}
        label="iOS style"
      />
      <Stack direction="row" spacing={1} sx={{ alignItems: 'center' }}>
        <Typography>Off</Typography>
        <AntSwitch
          defaultChecked
          slotProps={{ input: { 'aria-label': 'ant design' } }}
        />
        <Typography>On</Typography>
      </Stack>
    </FormGroup>
  );
}

🎨 영감을 찾고 있다면, MUI Treasury의 커스터마이즈 예제를 확인해 보세요.

레이블 배치 (Label placement)

레이블의 배치를 변경할 수 있어요:

import Switch from '@mui/material/Switch';
import FormGroup from '@mui/material/FormGroup';
import FormControlLabel from '@mui/material/FormControlLabel';
import FormControl from '@mui/material/FormControl';
import FormLabel from '@mui/material/FormLabel';

export default function FormControlLabelPosition() {
  return (
    <FormControl component="fieldset">
      <FormLabel component="legend">Label placement</FormLabel>
      <FormGroup aria-label="position" row>
        <FormControlLabel
          value="bottom"
          control={<Switch color="primary" />}
          label="Bottom"
          labelPlacement="bottom"
        />
        <FormControlLabel
          value="end"
          control={<Switch color="primary" />}
          label="End"
          labelPlacement="end"
        />
      </FormGroup>
    </FormControl>
  );
}

언제 사용하나요? (When to use)

접근성 (Accessibility)

  • 모든 폼 컨트롤에는 레이블이 있어야 해요, 라디오 버튼, 체크박스, 스위치도 포함합니다. 대부분의 경우 <label> 요소(FormControlLabel)로 이를 수행해요.
  • 레이블을 사용할 수 없을 때는 input 컴포넌트에 직접 속성을 추가해야 해요. 이 경우 slotProps.input prop을 통해 추가 속성(예: aria-label, aria-labelledby, title)을 적용할 수 있어요.
<Switch value="checkedA" slotProps={{ input: { 'aria-label': 'Switch A' } }} />

FormControl API

Demos (데모)

이 React 컴포넌트 사용에 대한 예시와 세부 사항은 컴포넌트 데모 페이지를 방문하세요:

Import (임포트)

import FormControl from '@mui/material/FormControl';
// or
import { FormControl } from '@mui/material';

Props

Name Type Default Required Description
children node - No
classes object - No Override or extend the styles applied to the component.
color 'primary' | 'secondary' | 'error' | 'info' | 'success' | 'warning' | string 'primary' No
component elementType - No
disabled bool false No
error bool false No
focused bool - No
fullWidth bool false No
hiddenLabel bool false No
margin 'dense' | 'none' | 'normal' 'none' No
required bool false No
size 'medium' | 'small' | string 'medium' No
sx Array<func | object | bool> | func | object - No The system prop that allows defining system overrides as well as additional CSS styles.
variant 'filled' | 'outlined' | 'standard' 'outlined' No

Note: The ref is forwarded to the root element (HTMLDivElement).

Any other props supplied will be provided to the root element (native element).

Theme default props (테마 기본 props)

MuiFormControl를 사용해서 테마로 이 컴포넌트의 기본 props를 변경할 수 있어요.

CSS

Rule name (규칙 이름)

Global class Rule name Description
- fullWidth Styles applied to the root element if fullWidth={true}.
- marginDense Styles applied to the root element if margin="dense".
- marginNormal Styles applied to the root element if margin="normal".
- root Styles applied to the root element.

Source code (소스 코드)

이 페이지에서 원하는 정보를 찾지 못했다면, 더 자세한 내용을 위해 컴포넌트의 구현을 살펴보는 것이 좋아요.

FormControlLabel API

Demos (데모)

이 React 컴포넌트 사용에 대한 예시와 세부 사항은 컴포넌트 데모 페이지를 방문하세요:

Import (임포트)

import FormControlLabel from '@mui/material/FormControlLabel';
// or
import { FormControlLabel } from '@mui/material';

Props

Name Type Default Required Description
control element - Yes
checked bool - No
classes object - No Override or extend the styles applied to the component.
disabled bool - No
disableTypography bool - No
inputRef ref - No
label node - No
labelPlacement 'bottom' | 'end' | 'start' | 'top' 'end' No
onChange function(event: React.SyntheticEvent) => void - No
required bool - No
slotProps { typography?: func | object } {} No
slots { typography?: elementType } {} No
sx Array<func | object | bool> | func | object - No The system prop that allows defining system overrides as well as additional CSS styles.
value any - No

Note: The ref is forwarded to the root element (HTMLLabelElement).

Any other props supplied will be provided to the root element (native element).

Theme default props (테마 기본 props)

MuiFormControlLabel를 사용해서 테마로 이 컴포넌트의 기본 props를 변경할 수 있어요.

Slots

Name Default Class Description
typography Typography - The component that renders the label.
This is unused if disableTypography is true.

CSS

Rule name (규칙 이름)

Global class Rule name Description
- asterisk Styles applied to the asterisk element.
.Mui-disabled - State class applied to the root element if disabled={true}.
.Mui-error - State class applied to the root element if error={true}.
- label Styles applied to the label's Typography component.
- labelPlacementBottom Styles applied to the root element if labelPlacement="bottom".
- labelPlacementEnd Styles applied to the root element if labelPlacement="end".
- labelPlacementStart Styles applied to the root element if labelPlacement="start".
- labelPlacementTop Styles applied to the root element if labelPlacement="top".
.Mui-required - State class applied to the root element if required={true}.
- root Styles applied to the root element.

Source code (소스 코드)

이 페이지에서 원하는 정보를 찾지 못했다면, 더 자세한 내용을 위해 컴포넌트의 구현을 살펴보는 것이 좋아요.

FormGroup API

Demos (데모)

이 React 컴포넌트 사용에 대한 예시와 세부 사항은 컴포넌트 데모 페이지를 방문하세요:

Import (임포트)

import FormGroup from '@mui/material/FormGroup';
// or
import { FormGroup } from '@mui/material';

Props

Name Type Default Required Description
children node - No
classes object - No Override or extend the styles applied to the component.
row bool false 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 ref is forwarded to the root element (HTMLDivElement).

Any other props supplied will be provided to the root element (native element).

Theme default props (테마 기본 props)

MuiFormGroup를 사용해서 테마로 이 컴포넌트의 기본 props를 변경할 수 있어요.

CSS

Rule name (규칙 이름)

Global class Rule name Description
.Mui-error - State class applied to the root element if error={true}.
- root Styles applied to the root element.
- row Styles applied to the root element if row={true}.

Source code (소스 코드)

이 페이지에서 원하는 정보를 찾지 못했다면, 더 자세한 내용을 위해 컴포넌트의 구현을 살펴보는 것이 좋아요.

FormLabel API

Demos (데모)

이 React 컴포넌트 사용에 대한 예시와 세부 사항은 컴포넌트 데모 페이지를 방문하세요:

Import (임포트)

import FormLabel from '@mui/material/FormLabel';
// or
import { FormLabel } from '@mui/material';

Props

Name Type Default Required Description
children node - No
classes object - No Override or extend the styles applied to the component.
color 'error' | 'info' | 'primary' | 'secondary' | 'success' | 'warning' | string - No
component elementType - No
disabled bool - No
error bool - No
filled bool - No
focused bool - No
required bool - 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 ref is forwarded to the root element (HTMLLabelElement).

Any other props supplied will be provided to the root element (native element).

Theme default props (테마 기본 props)

MuiFormLabel을 사용해서 테마로 이 컴포넌트의 기본 props를 변경할 수 있어요.

CSS

Rule name (규칙 이름)

Global class Rule name Description
- asterisk Styles applied to the asterisk element.
- colorSecondary Styles applied to the root element if the color is secondary.
.Mui-disabled - State class applied to the root element if disabled={true}.
.Mui-error - State class applied to the root element if error={true}.
- filled State class applied to the root element if filled={true}.
.Mui-focused - State class applied to the root element if focused={true}.
.Mui-required - State class applied to the root element if required={true}.
- root Styles applied to the root element.

Source code (소스 코드)

이 페이지에서 원하는 정보를 찾지 못했다면, 더 자세한 내용을 위해 컴포넌트의 구현을 살펴보는 것이 좋아요.

Switch API

Demos (데모)

이 React 컴포넌트 사용에 대한 예시와 세부 사항은 컴포넌트 데모 페이지를 방문하세요:

Import (임포트)

import Switch from '@mui/material/Switch';
// or
import { Switch } from '@mui/material';

Props

Name Type Default Required Description
checked bool - No
checkedIcon node - No
classes object - No Override or extend the styles applied to the component.
color 'default' | 'primary' | 'secondary' | 'error' | 'info' | 'success' | 'warning' | string 'primary' No
defaultChecked bool - No
disabled bool - No
disableRipple bool false No
edge 'end' | 'start' | false false No
icon node - No
id string - No
onChange function(event: React.ChangeEvent<HTMLInputElement>) => void - No
required bool false No
size 'medium' | 'small' | string 'medium' No
slotProps { input?: func | object, root?: func | object, switchBase?: func | object, thumb?: func | object, track?: func | object } {} No
slots { input?: elementType, root?: elementType, switchBase?: elementType, thumb?: elementType, track?: elementType } {} No
sx Array<func | object | bool> | func | object - No The system prop that allows defining system overrides as well as additional CSS styles.
value any - No

Note: The ref is forwarded to the root element (HTMLSpanElement).

Any other props supplied will be provided to the root element (IconButton).

Inheritance (상속)

위에 명시적으로 문서화되지는 않았지만, IconButton 컴포넌트의 props도 Switch에서 사용할 수 있어요.

Slots

Name Default Class Description
root 'span' .MuiSwitch-root The component that renders the root slot.
track 'span' .MuiSwitch-track The component that renders the track slot.
thumb 'span' .MuiSwitch-thumb The component that renders the thumb slot.
switchBase SwitchBase .MuiSwitch-switchBase The component that renders the switchBase slot.
input SwitchBaseInput .MuiSwitch-input The component that renders the switchBase's input slot.

CSS

Rule name (규칙 이름)

Global class Rule name Description
.Mui-checked - State class applied to the internal SwitchBase component's checked class.
- colorPrimary Styles applied to the internal SwitchBase component's root element if color="primary".
- colorSecondary Styles applied to the internal SwitchBase component's root element if color="secondary".
.Mui-disabled - State class applied to the internal SwitchBase component's disabled class.
- edgeEnd Styles applied to the root element if edge="end".
- edgeStart Styles applied to the root element if edge="start".
- sizeMedium Styles applied to the root element if size="medium".
- sizeSmall Styles applied to the root element if size="small".

Source code (소스 코드)

이 페이지에서 원하는 정보를 찾지 못했다면, 더 자세한 내용을 위해 컴포넌트의 구현을 살펴보는 것이 좋아요.

더 알아보기 (Learn more)