Menu

사용자가 옵션을 선택할 수 있는 요소로 버튼, 셀렉트, 필터 칩 등 다양한 트리거 요소에서 호출됩니다. 단일 선택과 복수 선택을 모두 지원하며, 추가 액션을 위한 Action area를 포함할 수 있습니다.

Variants

Menu는 3가지 variant를 지원합니다.

  • normal
  • radio
  • checkbox

normal, radio variant는 value 타입이 string 이고, checkbox variant는 value 타입이 Array<string> 입니다.

Normal

import { Button, Menu, MenuTrigger, MenuContent, MenuItem, MenuList } from '@wanteddev/wds';
import { IconRefresh } from '@wanteddev/wds-icon';

const Demo = () => {
  return (
    <Menu>
      <MenuTrigger>
        <Button>Click me</Button>
      </MenuTrigger>

      <MenuContent>
        <MenuList>
          <MenuItem value="item1">
            Item 1
          </MenuItem>
          <MenuItem value="item2">
            Item 2
          </MenuItem>
          <MenuItem value="item3">
            Item 3
          </MenuItem>
          <MenuItem value="item4">
            Item 4
          </MenuItem>
          <MenuItem value="item5">
            Item 5
          </MenuItem>
          <MenuItem value="item6">
            Item 6
          </MenuItem>
          <MenuItem value="item7">
            Item 7
          </MenuItem>
          <MenuItem value="item8">
            Item 8
          </MenuItem>
          <MenuItem value="item9">
            Item 9
          </MenuItem>
          <MenuItem value="item10">
            Item 10
          </MenuItem>
        </MenuList>
      </MenuContent>
    </Menu>
  )
}

export default Demo;

Radio

import { Button, Menu, MenuTrigger, MenuContent, MenuItem, MenuList } from '@wanteddev/wds';
import { IconRefresh } from '@wanteddev/wds-icon';

const Demo = () => {
  return (
    <Menu>
      <MenuTrigger>
        <Button>Click me</Button>
      </MenuTrigger>

      <MenuContent>
        <MenuList>
          <MenuItem variant="radio" value="item1">
            Item 1
          </MenuItem>
          <MenuItem variant="radio" value="item2">
            Item 2
          </MenuItem>
          <MenuItem variant="radio" value="item3">
            Item 3
          </MenuItem>
          <MenuItem variant="radio" value="item4">
            Item 4
          </MenuItem>
          <MenuItem variant="radio" value="item5">
            Item 5
          </MenuItem>
          <MenuItem variant="radio" value="item6">
            Item 6
          </MenuItem>
          <MenuItem variant="radio" value="item7">
            Item 7
          </MenuItem>
          <MenuItem variant="radio" value="item8">
            Item 8
          </MenuItem>
          <MenuItem variant="radio" value="item9">
            Item 9
          </MenuItem>
          <MenuItem variant="radio" value="item10">
            Item 10
          </MenuItem>
        </MenuList>
      </MenuContent>
    </Menu>
  )
}

export default Demo;

Checkbox

import { Button, Menu, MenuTrigger, MenuContent, MenuItem, MenuList } from '@wanteddev/wds';
import { IconRefresh } from '@wanteddev/wds-icon';

const Demo = () => {
  return (
    <Menu>
      <MenuTrigger>
        <Button>Click me</Button>
      </MenuTrigger>

      <MenuContent>
        <MenuList>
          <MenuItem variant="checkbox" value="item1">
            Item 1
          </MenuItem>
          <MenuItem variant="checkbox" value="item2">
            Item 2
          </MenuItem>
          <MenuItem variant="checkbox" value="item3">
            Item 3
          </MenuItem>
          <MenuItem variant="checkbox" value="item4">
            Item 4
          </MenuItem>
          <MenuItem variant="checkbox" value="item5">
            Item 5
          </MenuItem>
          <MenuItem variant="checkbox" value="item6">
            Item 6
          </MenuItem>
          <MenuItem variant="checkbox" value="item7">
            Item 7
          </MenuItem>
          <MenuItem variant="checkbox" value="item8">
            Item 8
          </MenuItem>
          <MenuItem variant="checkbox" value="item9">
            Item 9
          </MenuItem>
          <MenuItem variant="checkbox" value="item10">
            Item 10
          </MenuItem>
        </MenuList>
      </MenuContent>
    </Menu>
  )
}

export default Demo;

Anatomy

Menu 는 여러 요소들을 조합하여 사용합니다.

기본 구성은 아래와 같습니다.

<Menu>
  <MenuTrigger>
    <Button />
  </MenuTrigger>

  <MenuContent>
    <MenuList>  
      <MenuGroup>
        <MenuItem />
        <MenuItem />
      </MenuGroup>
      <MenuGroup>
        <MenuItem />
        <MenuItem />
      </MenuGroup>
    </MenuList>

    <MenuActionArea />
  </MenuContent>
</Menu>

Grouped items

MenuGroup 컴포넌트로 여러 옵션을 그룹화 할 수 있습니다.

import { Button, Menu, MenuTrigger, MenuContent, MenuGroup, MenuItem, MenuList } from '@wanteddev/wds';

const Demo = () => {
  return (
    <Menu>
      <MenuTrigger>
        <Button>Click me</Button>
      </MenuTrigger>

      <MenuContent>
        <MenuList>
          <MenuGroup title="Group 1">
            <MenuItem value="item1">
              Item 1
            </MenuItem>
            <MenuItem value="item2">
              Item 2
            </MenuItem>
            <MenuItem value="item3">
              Item 3
            </MenuItem>
          </MenuGroup>
          <MenuGroup title="Group 2">
            <MenuItem value="item4">
              Item 4
            </MenuItem>
            <MenuItem value="item5">
              Item 5
            </MenuItem>
            <MenuItem value="item6">
              Item 6
            </MenuItem>
          </MenuGroup>
          <MenuGroup title="Group 3">
            <MenuItem value="item7">
              Item 7
            </MenuItem>
            <MenuItem value="item8">
              Item 8
            </MenuItem>
            <MenuItem value="item9">
              Item 9
            </MenuItem>
          </MenuGroup>
        </MenuList>
      </MenuContent>
    </Menu>
  )
}

export default Demo;

With contents

MenuItem 컴포넌트에 leadingContent, trailingContent prop 을 사용하여 콘텐츠를 추가할 수 있습니다.

MenuItemContent 로 감싸서 사용합니다.

import { Button, Menu, MenuTrigger, MenuContent, MenuItem, MenuList, MenuItemContent, ContentBadge, IconButton, TextButton } from '@wanteddev/wds';
import { IconBlank } from '@wanteddev/wds-icon';

const Demo = () => {
  return (
    <Menu>
      <MenuTrigger>
        <Button>Click me</Button>
      </MenuTrigger>

      <MenuContent>
        <MenuList>
          <MenuItem
            variant="radio"
            value="item1"
            trailingContent={(
              <MenuItemContent variant="badge">
                <ContentBadge color="neutral" size="xsmall">
                  Badge
                </ContentBadge>
              </MenuItemContent>
            )}
          >
            Item 1
          </MenuItem>
          <MenuItem
            variant="radio"
            value="item2"
            leadingContent={(
              <MenuItemContent variant="icon-button">
                <IconButton size={24}>
                  <IconBlank />
                </IconButton>
              </MenuItemContent>
            )}
          >
            Item 2
          </MenuItem>
          <MenuItem
            variant="radio"
            value="item3"
            trailingContent={(
              <MenuItemContent variant="button">
                <TextButton size="small" color="assistive">
                  Button
                </TextButton>
              </MenuItemContent>
            )}
          >
            Item 3
          </MenuItem>
        </MenuList>
      </MenuContent>
    </Menu>
  )
}

export default Demo;

With action area

MenuActionArea 컴포넌트로 하단에 액션 영역을 추가할 수 있습니다.

MenuActionAreaContent 컴포넌트로 감싸서 MenuActionArea의 leadingContent, trailingContent prop 을 사용하여 콘텐츠를 추가할 수 있습니다.

import { Button, TextFieldContent, IconButton, FlexBox, Menu, MenuTrigger, TextButton, ContentBadge, MenuContent, MenuActionArea, MenuActionAreaContent, MenuGroup, MenuItem, FilterButton, MenuList } from '@wanteddev/wds';
import { IconRefresh, IconSend } from '@wanteddev/wds-icon';

const Demo = () => {
  return (
    <FlexBox gap="12px" flexWrap="wrap" alignItems="center">
      <Menu>
        <MenuTrigger>
          <Button>Buttons</Button>
        </MenuTrigger>

        <MenuContent>
          <MenuList>
            <MenuGroup title="Group 1">
              <MenuItem variant="checkbox" value="item1">
                Item 1
              </MenuItem>
              <MenuItem variant="checkbox" value="item2">
                Item 2
              </MenuItem>
              <MenuItem variant="checkbox" value="item3">
                Item 3
              </MenuItem>
            </MenuGroup>
            <MenuGroup title="Group 2">
              <MenuItem variant="checkbox" value="item4">
                Item 4
              </MenuItem>
              <MenuItem variant="checkbox" value="item5">
                Item 5
              </MenuItem>
              <MenuItem variant="checkbox" value="item6">
                Item 6
              </MenuItem>
            </MenuGroup>
            <MenuGroup title="Group 3">
              <MenuItem variant="checkbox" value="item7">
                Item 7
              </MenuItem>
              <MenuItem variant="checkbox" value="item8">
                Item 8
              </MenuItem>
              <MenuItem variant="checkbox" value="item9">
                Item 9
              </MenuItem>
            </MenuGroup>
          </MenuList>

          <MenuActionArea
            leadingContent={
              <MenuActionAreaContent variant="text-button">
                <TextButton
                  color="assistive"
                  size="small"
                  leadingContent={<IconRefresh />}
                >
                  Reset
                </TextButton>
              </MenuActionAreaContent>
            }
            trailingContent={
              <MenuActionAreaContent variant="button">
                <Button size="small">
                  Button
                </Button>
              </MenuActionAreaContent>
            }
          />
        </MenuContent>
      </Menu>

      <Menu>
        <MenuTrigger>
          <Button>Icon buttons</Button>
        </MenuTrigger>

        <MenuContent>
          <MenuList>
            <MenuGroup title="Group 1">
              <MenuItem variant="checkbox" value="item1">
                Item 1
              </MenuItem>
              <MenuItem variant="checkbox" value="item2">
                Item 2
              </MenuItem>
              <MenuItem variant="checkbox" value="item3">
                Item 3
              </MenuItem>
            </MenuGroup>
            <MenuGroup title="Group 2">
              <MenuItem variant="checkbox" value="item4">
                Item 4
              </MenuItem>
              <MenuItem variant="checkbox" value="item5">
                Item 5
              </MenuItem>
              <MenuItem variant="checkbox" value="item6">
                Item 6
              </MenuItem>
            </MenuGroup>
            <MenuGroup title="Group 3">
              <MenuItem variant="checkbox" value="item7">
                Item 7
              </MenuItem>
              <MenuItem variant="checkbox" value="item8">
                Item 8
              </MenuItem>
              <MenuItem variant="checkbox" value="item9">
                Item 9
              </MenuItem>
            </MenuGroup>
          </MenuList>

          <MenuActionArea
            leadingContent={
              <MenuActionAreaContent variant="button">
                <Button
                  variant="outlined"
                  color="assistive"
                  iconOnly
                  size="small"
                >
                  <IconRefresh />
                </Button> 
              </MenuActionAreaContent>
            }
            trailingContent={
              <MenuActionAreaContent variant="icon-button">
                <IconButton size="small" variant="solid">
                  <IconSend />
                </IconButton>
              </MenuActionAreaContent>
            }
          />
        </MenuContent>
      </Menu>

      <Menu>
        <MenuTrigger>
          <Button>Contents</Button>
        </MenuTrigger>

        <MenuContent>
          <MenuList>
            <MenuGroup title="Group 1">
              <MenuItem variant="checkbox" value="item1">
                Item 1
              </MenuItem>
              <MenuItem variant="checkbox" value="item2">
                Item 2
              </MenuItem>
              <MenuItem variant="checkbox" value="item3">
                Item 3
              </MenuItem>
            </MenuGroup>
            <MenuGroup title="Group 2">
              <MenuItem variant="checkbox" value="item4">
                Item 4
              </MenuItem>
              <MenuItem variant="checkbox" value="item5">
                Item 5
              </MenuItem>
              <MenuItem variant="checkbox" value="item6">
                Item 6
              </MenuItem>
            </MenuGroup>
            <MenuGroup title="Group 3">
              <MenuItem variant="checkbox" value="item7">
                Item 7
              </MenuItem>
              <MenuItem variant="checkbox" value="item8">
                Item 8
              </MenuItem>
              <MenuItem variant="checkbox" value="item9">
                Item 9
              </MenuItem>
            </MenuGroup>
          </MenuList>

          <MenuActionArea
            leadingContent={
              <MenuActionAreaContent variant="chip-filter">
                <FilterButton>Filter</FilterButton>
              </MenuActionAreaContent>
            }
            trailingContent={
              <MenuActionAreaContent variant="badge">
                <ContentBadge color="neutral">Badge</ContentBadge>
              </MenuActionAreaContent>
            }
          />
        </MenuContent>
      </Menu>
    </FlexBox>
  )
}

export default Demo;

API

NameTypesdefaultValue
defaultValue
string | string[]
-
value
string | string[]
-
onValueChange
(value: string | string[]) => void
-
children
ReactNode
-
open
boolean
-
defaultOpen
boolean
-
onOpenChange
(state: boolean) => void
-

children으로 요소를 지정합니다.

NameTypesdefaultValue
as
ElementType
-
children
ReactNode
-
sx
SxProp
-
forceMount
boolean
-
disablePortal
boolean
-
container
null | Element | DocumentFragment
-
wrapperProps
__type & { sx?: SxProp; ref?: React.Ref<HTMLDivElement> | undefined }
-
disableFocusScope
boolean
-
onDismiss
() => void
-
offset
number
-
position
"top-start" | "top-center" | "top-end" | "right-start" | "right-center" | "right-end" | "bottom-start" | "bottom-center" | "bottom-end" | "left-start" | "left-center" | "left-end"
"bottom-center"
referenceHidden
boolean
-
referenceHiddenOffsets
SideObject
-
setContext
(context: __type) => void
-
loop
boolean
-
trapped
boolean
-
trappedContent
boolean
-
onMountAutoFocus
(event: Event) => void
-
onUnmountAutoFocus
(event: Event) => void
-
onInteractOutside
(event: PointerDownOutsideEvent | FocusOutsideEvent) => void
-
onFocusOutside
(event: CustomEvent) => void
-
onPointerDownOutside
(event: CustomEvent) => void
-
disableOutsidePointerEvents
boolean
-
NameTypesdefaultValue
title
ReactNode
-
children
ReactNode
-
sx
SxProp
-
xl
Merge<Omit<FlexBoxDefaultProps, "children" | "sx">, { sx?: CSSInterpolation; }> | undefined
-
lg
Merge<Omit<FlexBoxDefaultProps, "children" | "sx">, { sx?: CSSInterpolation; }> | undefined
-
md
Merge<Omit<FlexBoxDefaultProps, "children" | "sx">, { sx?: CSSInterpolation; }> | undefined
-
sm
Merge<Omit<FlexBoxDefaultProps, "children" | "sx">, { sx?: CSSInterpolation; }> | undefined
-
xs
Merge<Omit<FlexBoxDefaultProps, "children" | "sx">, { sx?: CSSInterpolation; }> | undefined
-
flexDirection
Property.FlexDirection | undefined
-
flexWrap
Property.FlexWrap | undefined
-
justifyContent
Property.JustifyContent | undefined
-
alignItems
Property.AlignItems | undefined
-
alignContent
Property.AlignContent | undefined
-
order
Property.Order | undefined
-
flex
Property.Flex<string | number> | undefined
-
flexGrow
Property.FlexGrow | undefined
-
flexShrink
Property.FlexShrink | undefined
-
flexBasis
Property.FlexBasis<string | number> | undefined
-
alignSelf
Property.AlignSelf | undefined
-
gap
Property.Gap<string | number> | undefined
-
rowGap
Property.RowGap<string | number> | undefined
-
columnGap
Property.ColumnGap<string | number> | undefined
-
NameTypesdefaultValue
as
ElementType
-
variant
"normal" | "radio" | "checkbox"
"normal"
leadingContent
ReactNode
-
trailingContent
ReactNode
-
children
ReactNode
-
value *
string
-
sx
SxProp
-
disabled
boolean
-
divider
boolean
-
xl
Merge<Pick<ListCellDefaultProps, "verticalPadding" | "fillWidth" | "interactionPadding">, { sx?: CSSInterpolation; }> | undefined
-
lg
Merge<Pick<ListCellDefaultProps, "verticalPadding" | "fillWidth" | "interactionPadding">, { sx?: CSSInterpolation; }> | undefined
-
md
Merge<Pick<ListCellDefaultProps, "verticalPadding" | "fillWidth" | "interactionPadding">, { sx?: CSSInterpolation; }> | undefined
-
sm
Merge<Pick<ListCellDefaultProps, "verticalPadding" | "fillWidth" | "interactionPadding">, { sx?: CSSInterpolation; }> | undefined
-
xs
Merge<Pick<ListCellDefaultProps, "verticalPadding" | "fillWidth" | "interactionPadding">, { sx?: CSSInterpolation; }> | undefined
-
verticalPadding
"small" | "medium" | "large" | "none"
-
fillWidth
boolean
-
interactionPadding
Property.PaddingLeft<string | number> | undefined
-
ellipsis
boolean
-
selected
boolean
-
disableInteraction
boolean
-
textProps
Merge<TypographyProps, { caption?: ReactNode; captionProps?: ComponentProps<typeof Typography>; children?: ReactNode; sx?: SxProp; }>
-
flexDirection
Property.FlexDirection | undefined
-
flexWrap
Property.FlexWrap | undefined
-
justifyContent
Property.JustifyContent | undefined
-
alignItems
Property.AlignItems | undefined
-
alignContent
Property.AlignContent | undefined
-
order
Property.Order | undefined
-
flex
Property.Flex<string | number> | undefined
-
flexGrow
Property.FlexGrow | undefined
-
flexShrink
Property.FlexShrink | undefined
-
flexBasis
Property.FlexBasis<string | number> | undefined
-
alignSelf
Property.AlignSelf | undefined
-
gap
Property.Gap<string | number> | undefined
-
rowGap
Property.RowGap<string | number> | undefined
-
columnGap
Property.ColumnGap<string | number> | undefined
-
NameTypesdefaultValue
variant
"button" | "switch" | "chevron" | "icon" | "radio" | "checkbox" | "icon-button" | "badge" | "avatar" | "large-icon" | "value" | "thumbnail" | "custom"
-
disabled
boolean
-
chevron
boolean
-
children
ReactNode
-
sx
SxProp
-
NameTypesdefaultValue
leadingContent
ReactNode
-
trailingContent
ReactNode
-
children
ReactNode
-
sx
SxProp
-
NameTypesdefaultValue
variant
"button" | "icon" | "icon-button" | "badge" | "custom" | "text-button" | "chip-filter"
"custom"
children
ReactNode
-
sx
SxProp
-

© 2026 Wanted Lab, Inc.