Checkbox

Checkbox (체크박스)

Checkbox 컴포넌트는 사용자로부터 불리언(boolean) 입력을 받는 컴포넌트예요. 네이티브 input[type="checkbox"]를 기반으로 하며 기본적으로 접근 가능해요.

출처: 문서

본문

Checkbox로 선택 여부를 나타내는 입력을 만들 수 있어요.

import { Checkbox } from '@mantine/core';

function Demo() {
  return (
    <Checkbox
      defaultChecked
      label="I agree to sell my privacy"
    />
  );
}

제어 상태 (Controlled state)

checked와 onChange prop으로 Checkbox 상태를 제어할 수 있어요.

import { useState } from 'react';
import { Checkbox } from '@mantine/core';

function Demo() {
  const [checked, setChecked] = useState(false);
  return (
    <Checkbox
      checked={checked}
      onChange={(event) => setChecked(event.currentTarget.checked)}
    />
  );
}

읽기 전용 (Read only)

readOnly prop을 설정하면 사용자 상호작용으로 체크박스 값을 바꿀 수 없게 돼요. 체크박스는 현재 값을 계속 표시하고 checked prop의 프로그래밍 업데이트를 반영하지만, 클릭(또는 Space 키)해도 상태가 토글되지 않고 onChange 핸들러도 호출되지 않아요.

import { useState } from 'react';
import { Checkbox } from '@mantine/core';

function Demo() {
  const [checked, setChecked] = useState(true);
  return (
    <>
      <Checkbox checked={checked} readOnly label="Read only checkbox" />
      <button type="button" onClick={() => setChecked((c) => !c)}>
        Toggle from outside
      </button>
    </>
  );
}

@mantine/form과 함께 사용하기

@mantine/form과 Checkbox를 사용하는 예시:

import { Button, Checkbox } from '@mantine/core';
import { isNotEmpty, useForm } from '@mantine/form';

function Demo() {
  const form = useForm({
    mode: 'uncontrolled',
    initialValues: { terms: false },
    validate: {
      terms: isNotEmpty('You must accept terms and conditions'),
    },
  });

  return (
    <form onSubmit={form.onSubmit((values) => console.log(values))}>
      <Checkbox
        label="I accept the terms and conditions"
        key={form.key('terms')}
        {...form.getInputProps('terms', { type: 'checkbox' })}
      />

      <Button type="submit" mt="md">
        Submit
      </Button>
    </form>
  );
}

비제어 폼과 함께 사용하기

Checkbox는 네이티브 input[type="checkbox"]처럼 비제어 폼에서도 사용할 수 있어요. 폼 제출 시 FormData 객체에 체크박스 값을 포함하려면 name 속성을 설정해요. 비제어 폼에서 초기 체크 상태를 제어하려면 defaultChecked prop을 사용해요.

import { Checkbox } from '@mantine/core';

function Demo() {
  return (
    <form
      onSubmit={(event) => {
        event.preventDefault();
        const formData = new FormData(event.currentTarget);
        console.log('Checkbox value:', !!formData.get('terms'));
      }}
    >
      <Checkbox label="Accept terms and conditions" name="terms" defaultChecked />
      <button type="submit">Submit</button>
    </form>
  );
}

상태 (States)

Checkbox는 variant(기본/outline), 체크, indeterminate, disabled 상태를 지원해요.

import { Checkbox, Stack } from '@mantine/core';

function Demo() {
  return (
    <Stack>
      <Checkbox checked={false} onChange={() => {}} label="Default checkbox" />
      <Checkbox checked={false} onChange={() => {}} indeterminate label="Indeterminate checkbox" />
      <Checkbox checked onChange={() => {}} label="Checked checkbox" />
      <Checkbox checked variant="outline" onChange={() => {}} label="Outline checked checkbox" />
      <Checkbox
        variant="outline"
        onChange={() => {}}
        indeterminate
        label="Outline indeterminate checkbox"
      />
      <Checkbox disabled label="Disabled checkbox" />
      <Checkbox disabled checked onChange={() => {}} label="Disabled checked checkbox" />
      <Checkbox disabled indeterminate label="Disabled indeterminate checkbox" />
    </Stack>
  );
}

오류 상태 (Error state)

error prop으로 체크박스 라벨 아래에 오류 메시지를 표시할 수 있어요. 오류 메시지 없이 체크박스에 오류 스타일을 적용하려면 boolean error prop을 사용해요. 오류 스타일 없이 오류 메시지만 표시하려면 withErrorStyles={false}를 설정해요.

import { Checkbox, Stack } from '@mantine/core';

function Demo() {
  return (
    <Stack>
      <Checkbox label="With boolean error" error />
      <Checkbox label="With error message" error="Must be checked" />
      <Checkbox label="With error message" error="No error styles" withErrorStyles={false} />
    </Stack>
  );
}

아이콘 바꾸기 (Change icons)

icon prop으로 체크 아이콘을 바꿀 수 있어요.

import { Checkbox, CheckboxIconComponent } from '@mantine/core';
import { BiohazardIcon, RadioactiveIcon } from '@phosphor-icons/react';

const CheckboxIcon: CheckboxIconComponent = ({ indeterminate, ...others }) =>
  indeterminate ? <RadioactiveIcon {...others} /> : <BiohazardIcon {...others} />;

function Demo() {
  return (
    <>
      <Checkbox icon={CheckboxIcon} label="Custom icon" defaultChecked />
      <Checkbox icon={CheckboxIcon} label="Custom icon: indeterminate" indeterminate mt="sm" />
    </>
  );
}

아이콘 색상 바꾸기 (Change icon color)

iconColor prop으로 아이콘 색상을 바꿀 수 있어요. theme.colors의 색상을 참조하거나 유효한 CSS 색상을 사용할 수 있어요.

import { Checkbox } from '@mantine/core';

function Demo() {
  return (
    <Checkbox
      defaultChecked
      color="lime.4"
      iconColor="dark.8"
      size="md"
      label="Bright lime checkbox"
    />
  );
}

Indeterminate 상태 (Indeterminate state)

Checkbox는 indeterminate 상태를 지원해요. indeterminate prop이 설정되면 checked prop은 무시돼요(체크박스는 항상 체크 스타일을 가짐).

import { useListState, randomId } from '@mantine/hooks';
import { Checkbox } from '@mantine/core';

const initialValues = [
  { label: 'Receive email notifications', checked: false, key: randomId() },
  { label: 'Receive sms notifications', checked: false, key: randomId() },
  { label: 'Receive push notifications', checked: false, key: randomId() },
];

export function IndeterminateCheckbox() {
  const [values, handlers] = useListState(initialValues);

  const allChecked = values.every((value) => value.checked);
  const indeterminate = values.some((value) => value.checked) && !allChecked;

  const items = values.map((value, index) => (
    <Checkbox
      mt="xs"
      ml={33}
      label={value.label}
      key={value.key}
      checked={value.checked}
      onChange={(event) => handlers.setItemProp(index, 'checked', event.currentTarget.checked)}
    />
  ));

  return (
    <>
      <Checkbox
        checked={allChecked}
        indeterminate={indeterminate}
        label="Receive all notifications"
        onChange={() =>
          handlers.setState((current) =>
            current.map((value) => ({ ...value, checked: !allChecked }))
          )
        }
      />
      {items}
    </>
  );
}

라벨에 React 노드를 전달해 링크를 포함할 수 있어요.

import { Checkbox, Anchor } from '@mantine/core';

function Demo() {
  return (
    <Checkbox
      label={
        <>
          I accept{' '}
          <Anchor href="https://mantine.dev" target="_blank" inherit>
            terms and conditions
          </Anchor>
        </>
      }
    />
  );
}

툴팁이 있는 Checkbox

refProp으로 툴팁이 연결되는 대상 요소를 바꿀 수 있어요.

  • refProp이 설정되지 않으면 툴팁은 체크박스 input에 연결돼요
  • refProp="rootRef"가 설정되면 툴팁은 루트 요소(라벨·input·다른 요소 포함)에 연결돼요
import { Tooltip, Checkbox } from '@mantine/core';

function Demo() {
  return (
    <>
      <Tooltip label="Checkbox with tooltip">
        <Checkbox label="Tooltip on checkbox only" />
      </Tooltip>

      <Tooltip label="Checkbox with tooltip" refProp="rootRef">
        <Checkbox label="Tooltip the entire element" mt="md" />
      </Tooltip>
    </>
  );
}

포인터 커서 (Pointer cursor)

기본적으로 체크박스 input과 라벨은 cursor: default(네이티브 input[type="checkbox"]와 동일)예요. 커서를 포인터로 바꾸려면 theme에 cursorType을 설정해요.

import { MantineProvider, createTheme, Checkbox } from '@mantine/core';

const theme = createTheme({
  cursorType: 'pointer',
});

function Demo() {
  return (
    <>
      <Checkbox label="Default cursor" />

      <MantineProvider theme={theme}>
        <Checkbox label="Pointer cursor" mt="md" />
      </MantineProvider>
    </>
  );
}

autoContrast

Checkbox는 autoContrast prop과 theme.autoContrast를 지원해요. Checkbox나 테마에 autoContrast가 설정되면, color prop에 지정된 값과 충분한 대비를 가지도록 콘텐츠 색상이 조정돼요.

주의: autoContrast 기능은 배경색을 바꾸기 위해 color prop을 사용할 때만 동작해요. autoContrast는 filled variant에서만 동작해요.

import { Checkbox, Stack } from '@mantine/core';

function Demo() {
  return (
    <Stack>
      <Checkbox checked label="regular checkbox" size="lg" color="lime.4" />
      <Checkbox autoContrast checked label="autoContrast checkbox" size="lg" color="lime.4" />
    </Stack>
  );
}

커스텀 크기 추가

data-size 속성으로 커스텀 크기를 추가할 수 있어요.

import { MantineProvider, Checkbox, createTheme } from '@mantine/core';
import classes from './Demo.module.css';

const theme = createTheme({
  components: {
    Checkbox: Checkbox.extend({ classNames: classes }),
  },
});

function Demo() {
  return (
    <MantineProvider theme={theme}>
      <Checkbox size="xxs" label="Extra small checkbox" />
      <Checkbox size="xxl" label="Extra large checkbox" mt="md" />
    </MantineProvider>
  );
}

루트 요소에 prop 추가하기

컴포넌트에 전달된 모든 prop은 input 요소로 전달돼요. 루트 요소에 prop을 추가하려면 wrapperProps를 사용해요. 다음 예시에서:

  • data-testid="wrapper"는 루트 요소에 추가돼요
  • data-testid="input"은 input 요소에 추가돼요
import { Checkbox } from '@mantine/core';

function Demo() {
  return <Checkbox wrapperProps={{ 'data-testid': 'wrapper' }} data-testid="input" />;
}

Checkbox.Group

Checkbox.Group은 여러 체크박스의 상태를 관리해요. value와 onChange prop을 받아 그룹 안의 체크박스 상태를 제어해요. value prop은 문자열 배열이어야 하며, 각 문자열은 체크박스의 값이에요. onChange prop은 새 값을 문자열 배열로 받는 함수여야 해요.

import { useState } from 'react';
import { Checkbox } from '@mantine/core';

function Demo() {
  const [value, setValue] = useState<string[]>([]);

  return (
    <Checkbox.Group value={value} onChange={setValue}>
      <Checkbox value="react" label="React" />
      <Checkbox value="svelte" label="Svelte" />
    </Checkbox.Group>
  );
}

Checkbox.Group 컴포넌트는 모든 Input.Wrapper props를 지원해요.

import { Checkbox, Group } from '@mantine/core';

function Demo() {
  return (
    <Checkbox.Group
      defaultValue={['react']}
      label="Select your favorite frameworks/libraries"
      description="This is anonymous"
      withAsterisk
    >
      <Group mt="xs">
        <Checkbox value="react" label="React" />
        <Checkbox value="svelte" label="Svelte" />
        <Checkbox value="ng" label="Angular" />
        <Checkbox value="vue" label="Vue" />
      </Group>
    </Checkbox.Group>
  );
}

Checkbox.Group 비활성화

disabled prop으로 그룹 안의 모든 체크박스를 비활성화할 수 있어요.

import { Checkbox } from '@mantine/core';

function Demo() {
  return (
    <Checkbox.Group disabled>
      <Stack>
        <Checkbox value="react" label="React" />
        <Checkbox value="svelte" label="Svelte" />
        <Checkbox value="angular" label="Angular" />
        <Checkbox value="vue" label="Vue" />
      </Stack>
    </Checkbox.Group>
  );
}

maxSelectedValues

maxSelectedValues prop으로 Checkbox.Group에서 선택할 수 있는 값의 수를 제한할 수 있어요. 한도에 도달하면 나머지 체크박스는 비활성화되어 선택할 수 없어요.

import { Checkbox, Group } from '@mantine/core';

function Demo() {
  return (
    <Checkbox.Group defaultValue={['react']} maxSelectedValues={2}>
      <Group>
        <Checkbox value="react" label="React" />
        <Checkbox value="svelte" label="Svelte" />
        <Checkbox value="ng" label="Angular" />
        <Checkbox value="vue" label="Vue" />
      </Group>
    </Checkbox.Group>
  );
}

@mantine/form과 함께 쓰는 Checkbox.Group

import { Button, Checkbox, Group } from '@mantine/core';
import { hasLength, useForm } from '@mantine/form';

interface FormValues {
  frameworks: string[];
}

function Demo() {
  const form = useForm<FormValues>({
    mode: 'uncontrolled',
    initialValues: { frameworks: [] },
    validate: {
      frameworks: hasLength({ min: 1 }, 'Select at least one framework'),
    },
  });

  return (
    <form onSubmit={form.onSubmit((values) => console.log(values))}>
      <Checkbox.Group
        {...form.getInputProps('frameworks')}
        key={form.key('frameworks')}
        label="Select your favorite frameworks/libraries"
        withAsterisk
      >
        <Group my={5}>
          <Checkbox value="react" label="React" />
          <Checkbox value="svelte" label="Svelte" />
          <Checkbox value="ng" label="Angular" />
          <Checkbox value="vue" label="Vue" />
        </Group>
      </Checkbox.Group>

      <Button type="submit" mt="md">
        Submit
      </Button>
    </form>
  );
}

비제어 폼과 함께 쓰는 Checkbox.Group

Checkbox.Group은 비제어 폼에서 사용할 수 있어요. hiddenInputValuesSeparator prop으로 선택된 모든 값을 단일 문자열로 결합하는 숨겨진 input을 렌더링해요.

비제어 폼에서 사용하기 위한 props:

  • name – 숨겨진 input에 전달되는 name 속성
  • hiddenInputValuesSeparator – 선택된 값을 단일 문자열로 결합하는 데 사용하는 문자열, 기본값은 ','
  • hiddenInputProps – 숨겨진 input에 전달되는 추가 props
export function UncontrolledForm() {
  return (
    <form
      onSubmit={(event) => {
        event.preventDefault();
        const formData = new FormData(event.currentTarget);
        console.log('Checkbox group value:', formData.get('frameworks'));
      }}
    >
      <Checkbox.Group label="Frameworks" name="frameworks" hiddenInputValuesSeparator="|">
        <Checkbox label="React" value="react" />
        <Checkbox label="Angular" value="ng" />
      </Checkbox.Group>
      <button type="submit">Submit</button>
    </form>
  );
}

Checkbox.Indicator

Checkbox.Indicator는 Checkbox 컴포넌트와 모양이 완전히 같지만 시맨틱 의미가 없고 체크박스 상태의 시각적 표현일 뿐이에요. indicator와 관련된 상호작용 없이 체크박스 상태를 표시해야 하는 모든 곳에서 사용할 수 있어요. 버튼 기반 카드, 트리 등에서 유용해요.

주의: Checkbox.Indicator는 키보드로 포커스하거나 선택할 수 없어요. 접근 가능하지 않으며 Checkbox 컴포넌트의 대체재로 사용해서는 안 돼요.

import { Checkbox, Group } from '@mantine/core';

function Demo() {
  return (
    <Group>
      <Checkbox.Indicator />
      <Checkbox.Indicator checked />
      <Checkbox.Indicator indeterminate />
      <Checkbox.Indicator disabled />
      <Checkbox.Indicator disabled checked />
      <Checkbox.Indicator disabled indeterminate />
    </Group>
  );
}

Checkbox.Card 컴포넌트

Checkbox.Card 컴포넌트는 Checkbox의 대체재로 커스텀 카드/버튼/기타 체크박스처럼 동작하는 것을 만드는 데 사용할 수 있어요. 루트 요소에 role="checkbox" 속성이 있으며 기본적으로 접근 가능하고 input[type="checkbox"]와 동일한 키보드 상호작용을 지원해요.

import { useState } from 'react';
import { Checkbox, Group, Text } from '@mantine/core';
import classes from './Demo.module.css';

function Demo() {
  const [checked, setChecked] = useState(false);

  return (
    <Checkbox.Card
      className={classes.root}
      checked={checked}
      onClick={() => setChecked((c) => !c)}
    >
      <Group wrap="nowrap" align="flex-start">
        <Checkbox.Indicator />
        <div>
          <Text className={classes.label}>mantine/core</Text>
          <Text className={classes.description}>
            Core components library: inputs, buttons, overlays, etc.
          </Text>
        </div>
      </Group>
    </Checkbox.Card>
  );
}

Checkbox.Card를 Checkbox 컴포넌트와 같은 방식으로 Checkbox.Group과 함께 사용할 수 있어요.

import { useState } from 'react';
import { Checkbox, Group, Stack, Text } from '@mantine/core';
import classes from './Demo.module.css';

const data = [
  {
    name: 'mantine/core',
    description: 'Core components library: inputs, buttons, overlays, etc.',
  },
  { name: 'mantine/hooks', description: 'Collection of reusable hooks for React applications.' },
  { name: 'mantine/notifications', description: 'Notifications system' },
];

function Demo() {
  const [value, setValue] = useState<string[]>([]);

  const cards = data.map((item) => (
    <Checkbox.Card className={classes.root} value={item.name} key={item.name}>
      <Group wrap="nowrap" align="flex-start">
        <Checkbox.Indicator />
        <div>
          <Text className={classes.label}>{item.name}</Text>
          <Text className={classes.description}>{item.description}</Text>
        </div>
      </Group>
    </Checkbox.Card>
  ));

  return (
    <>
      <Checkbox.Group
        value={value}
        onChange={setValue}
        label="Pick packages to install"
        description="Choose all packages that you will need in your application"
      >
        <Stack pt="md" gap="xs">
          {cards}
        </Stack>
      </Checkbox.Group>

      <Text fz="xs" mt="md">
        CurrentValue: {value.join(', ') || '–'}
      </Text>
    </>
  );
}

요소 ref 가져오기

import { useRef } from 'react';
import { Checkbox } from '@mantine/core';

function Demo() {
  const ref = useRef<HTMLInputElement>(null);
  return <Checkbox ref={ref} />;
}

위 예시는 체크박스 input 요소의 ref를 얻는 방법을 보여 줘요. 루트 요소의 ref를 얻으려면 rootRef prop을 사용해요.

import { useRef } from 'react';
import { Checkbox } from '@mantine/core';

function Demo() {
  const ref = useRef<HTMLDivElement>(null);
  return <Checkbox rootRef={ref} />;
}

Styles API

Checkbox는 Styles API를 지원해요. classNames prop으로 내부 요소에 스타일을 추가할 수 있어요.

주요 선택자는 다음과 같아요.

  • root – 루트 요소
  • input – input 요소(input[type="checkbox"])
  • icon – 체크마크와 indeterminate 상태 아이콘을 표시하는 데 사용되는 체크박스 아이콘
  • inner – icon과 input을 감싸는 wrapper
  • body – 다른 모든 요소를 포함하는 input body
  • labelWrapper – label, description, error 포함
  • label – 라벨 요소
  • description – 라벨 아래에 표시되는 설명
  • error – 라벨 아래에 표시되는 오류 메시지

예시: 행 선택이 있는 Table

import { useState } from 'react';
import { Table, Checkbox } from '@mantine/core';

const elements = [
  { position: 6, mass: 12.011, symbol: 'C', name: 'Carbon' },
  { position: 7, mass: 14.007, symbol: 'N', name: 'Nitrogen' },
  { position: 39, mass: 88.906, symbol: 'Y', name: 'Yttrium' },
  { position: 56, mass: 137.33, symbol: 'Ba', name: 'Barium' },
  { position: 58, mass: 140.12, symbol: 'Ce', name: 'Cerium' },
];

function Demo() {
  const [selectedRows, setSelectedRows] = useState<number[]>([]);

  const rows = elements.map((element) => (
    <Table.Tr
      key={element.name}
      bg={selectedRows.includes(element.position) ? 'var(--mantine-color-blue-light)' : undefined}
    >
      <Table.Td>
        <Checkbox
          aria-label="Select row"
          checked={selectedRows.includes(element.position)}
          onChange={(event) =>
            setSelectedRows(
              event.currentTarget.checked
                ? [...selectedRows, element.position]
                : selectedRows.filter((position) => position !== element.position)
            )
          }
        />
      </Table.Td>
      <Table.Td>{element.position}</Table.Td>
      <Table.Td>{element.name}</Table.Td>
      <Table.Td>{element.symbol}</Table.Td>
      <Table.Td>{element.mass}</Table.Td>
    </Table.Tr>
  ));

  return (
    <Table>
      <Table.Thead>
        <Table.Tr>
          <Table.Th />
          <Table.Th>Element position</Table.Th>
          <Table.Th>Element name</Table.Th>
          <Table.Th>Symbol</Table.Th>
          <Table.Th>Atomic mass</Table.Th>
        </Table.Tr>
      </Table.Thead>
      <Table.Tbody>{rows}</Table.Tbody>
    </Table>
  );
}

예시: Styles API로 커스터마이즈

import { useState } from 'react';
import { Checkbox } from '@mantine/core';
import classes from './Demo.module.css';

function Demo() {
  const [checked, setChecked] = useState(false);

  return (
    <Checkbox
      classNames={classes}
      label="Checkbox button"
      checked={checked}
      onChange={(event) => setChecked(event.currentTarget.checked)}
      wrapperProps={{
        onClick: () => setChecked((c) => !c),
      }}
    />
  );
}

wrapperProps

대부분의 Checkbox props는 input 요소로 전달돼요. 루트 요소에 props를 전달하려면 wrapperProps prop을 사용해요.

import { Checkbox } from '@mantine/core';

function Demo() {
  return (
    <Checkbox
      label="My checkbox"
      wrapperProps={{ 'data-root-element': true }}
    />
  );
}

id 속성

기본적으로 Checkbox는 input 요소를 라벨과 연결하기 위해 랜덤 id 속성을 생성해요. id prop으로 직접 id를 제공할 수 있어요. 이 id는 input 요소의 id 속성과 라벨 요소의 htmlFor 속성에서 사용돼요.

import { Checkbox } from '@mantine/core';

function Demo() {
  return <Checkbox id="my-checkbox" label="My checkbox" />;
}

접근성 (Accessibility)

Checkbox 컴포넌트는 네이티브 input[type="checkbox"] 요소를 기반으로 하므로 기본적으로 접근 가능해요.

aria-label 또는 label prop을 설정해 스크린 리더에서 체크박스를 접근 가능하게 만들어요.

import { Checkbox } from '@mantine/core';

// Not ok, input is not labeled
function Bad() {
  return <Checkbox />;
}

// Ok, input is labelled by aria-label
function GoodAriaLabel() {
  return <Checkbox aria-label="My checkbox" />;
}

// Ok, input is labelled by label element
function GoodLabel() {
  return <Checkbox label="My checkbox" />;
}

더 알아보기 (Learn more)

  • Chip — 선택 가능한 태그 컴포넌트
  • Switch — 스위치 컴포넌트
  • Input — 입력 컴포넌트 기반 문서