Popup

사용자의 즉각적인 주의와 응답이 필요한 상황에서 사용하는 모달 요소입니다. 사용자의 현재 작업을 중단 시키고 팝업 내용에 집중하도록 하며, 필수적인 결정이나 확인이 필요한 경우에 사용합니다.

Basic popup

Popup overlay를 표시합니다.

breakpoint에 따라 variant를 override 하여 Bottom sheet 와 쉽게 통합할 수 있습니다.

import {
  Button,
  Modal,
  ActionArea,
  ActionAreaButton,
  ModalContainer,
  ModalContent,
  ModalContentItem,
  ModalDescription,
  ModalHeading,
  ModalNavigation,
  ModalSummary,
  ModalTrigger,
} from '@wanteddev/wds';

const Demo = () => {
  return (
    <Modal>
      <ModalTrigger>
        <Button>Open</Button>
      </ModalTrigger>

      <ModalContainer variant="popup">
        <ModalNavigation>
          Title
        </ModalNavigation>

        <ModalContent>
          <ModalContentItem>
            <ModalHeading>Heading</ModalHeading>
            <ModalSummary>Summary</ModalSummary>
            <ModalDescription>Description</ModalDescription>
          </ModalContentItem>
        </ModalContent>

        <ActionArea>
          <ActionAreaButton>
            Action
          </ActionAreaButton>
        </ActionArea>
      </ModalContainer>
    </Modal>
  )
}

export default Demo;

Anatomy

Popup 은 여러 컴포넌트를 조합해서 사용합니다.

ModalTrigger 는 선택적으로 사용할 수 있습니다.

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

<Modal>
  <ModalTrigger>
    <Button />
  </ModalTrigger>

  <ModalContainer>
    <ModalNavigation />

    <ModalContent>
      <ModalContentItem>
        <ModalHeading />
        <ModalSummary />
        <ModalDescription />
      </ModalContentItem>
    </ModalContent>

    <ActionArea>
      <ActionAreaButton />
    </ActionArea>
  </ModalContainer>
</Modal>

Sizes

4가지 사이즈를 사용할 수 있습니다.

  • small (deprecated)
  • medium (default)
  • large
  • xlarge
import {
  FlexBox,
  Button,
  Modal,
  ActionArea,
  ActionAreaButton,
  ModalContainer,
  ModalContent,
  ModalContentItem,
  ModalDescription,
  ModalHeading,
  ModalNavigation,
  ModalSummary,
  ModalTrigger,
} from '@wanteddev/wds';

const Demo = () => {
  return (
    <FlexBox gap="12px" flexWrap="wrap">
      <Modal>
        <ModalTrigger>
          <Button>Small</Button>
        </ModalTrigger>

        <ModalContainer variant="popup" size="small">
          <ModalNavigation>
            Title
          </ModalNavigation>

          <ModalContent>
            <ModalContentItem>
              <ModalHeading>Heading</ModalHeading>
              <ModalSummary>Summary</ModalSummary>
              <ModalDescription>Description</ModalDescription>
            </ModalContentItem>
          </ModalContent>

          <ActionArea>
            <ActionAreaButton>
              Action
            </ActionAreaButton>
          </ActionArea>
        </ModalContainer>
      </Modal>

      <Modal>
        <ModalTrigger>
          <Button>Medium</Button>
        </ModalTrigger>

        <ModalContainer variant="popup" size="medium">
          <ModalNavigation>
            Title
          </ModalNavigation>

          <ModalContent>
            <ModalContentItem>
              <ModalHeading>Heading</ModalHeading>
              <ModalSummary>Summary</ModalSummary>
              <ModalDescription>Description</ModalDescription>
            </ModalContentItem>
          </ModalContent>

          <ActionArea>
            <ActionAreaButton>
              Action
            </ActionAreaButton>
          </ActionArea>
        </ModalContainer>
      </Modal>

      <Modal>
        <ModalTrigger>
          <Button>Large</Button>
        </ModalTrigger>

        <ModalContainer variant="popup" size="large">
          <ModalNavigation>
            Title
          </ModalNavigation>

          <ModalContent>
            <ModalContentItem>
              <ModalHeading>Heading</ModalHeading>
              <ModalSummary>Summary</ModalSummary>
              <ModalDescription>Description</ModalDescription>
            </ModalContentItem>
          </ModalContent>

          <ActionArea>
            <ActionAreaButton>
              Action
            </ActionAreaButton>
          </ActionArea>
        </ModalContainer>
      </Modal>

      <Modal>
        <ModalTrigger>
          <Button>Xlarge</Button>
        </ModalTrigger>

        <ModalContainer variant="popup" size="xlarge">
          <ModalNavigation>
            Title
          </ModalNavigation>

          <ModalContent>
            <ModalContentItem>
              <ModalHeading>Heading</ModalHeading>
              <ModalSummary>Summary</ModalSummary>
              <ModalDescription>Description</ModalDescription>
            </ModalContentItem>
          </ModalContent>

          <ActionArea>
            <ActionAreaButton>
              Action
            </ActionAreaButton>
          </ActionArea>
        </ModalContainer>
      </Modal>
    </FlexBox>
  )
}

export default Demo;

Resize

resize 옵션을 사용하면 Bottom sheet가 차지하는 높이를 조정할 수 있습니다.

  • hug (default)
  • fixed
import {
  FlexBox,
  Button,
  Modal,
  ActionArea,
  ActionAreaButton,
  ModalContainer,
  ModalContent,
  ModalContentItem,
  ModalDescription,
  ModalHeading,
  ModalNavigation,
  ModalSummary,
  ModalTrigger,
} from '@wanteddev/wds';

const Demo = () => {
  return (
    <FlexBox gap="12px" flexWrap="wrap">
      <Modal>
        <ModalTrigger>
          <Button>Hug</Button>
        </ModalTrigger>

        <ModalContainer variant="popup" resize="hug">
          <ModalNavigation>
            Hug
          </ModalNavigation>

          <ModalContent>
            <ModalContentItem>
              <ModalHeading>Heading</ModalHeading>
              <ModalSummary>Summary</ModalSummary>
              <ModalDescription>Description</ModalDescription>
            </ModalContentItem>
          </ModalContent>

          <ActionArea>
            <ActionAreaButton>
              Action
            </ActionAreaButton>
          </ActionArea>
        </ModalContainer>
      </Modal>

      <Modal>
        <ModalTrigger>
          <Button>Fixed</Button>
        </ModalTrigger>

        <ModalContainer variant="popup" resize="fixed">
          <ModalNavigation>
            Fixed
          </ModalNavigation>

          <ModalContent>
            <ModalContentItem>
              <ModalHeading>Heading</ModalHeading>
              <ModalSummary>Summary</ModalSummary>
              <ModalDescription>Description</ModalDescription>
            </ModalContentItem>
          </ModalContent>

          <ActionArea>
            <ActionAreaButton>
              Action
            </ActionAreaButton>
          </ActionArea>
        </ModalContainer>
      </Modal>
    </FlexBox>
  )
}

export default Demo;

4가지 variant의 Navigation을 사용할 수 있습니다.

  • normal
  • emphasized
  • floating
  • display

또한 ModalNavigation 의 leadingContent, trailingContent로 콘텐츠를 추가할 수 있으며 ModalNavigationButton 으로 감싸서 사용합니다.

import {
  FlexBox,
  Button,
  Modal,
  ActionArea,
  ActionAreaButton,
  ModalContainer,
  ModalContent,
  ModalContentItem,
  ModalDescription,
  ModalHeading,
  ModalNavigation,
  ModalSummary,
  ModalTrigger,
} from '@wanteddev/wds';

const Demo = () => {
  return (
    <FlexBox gap="12px" flexWrap="wrap">
      <Modal>
        <ModalTrigger>
          <Button>Normal</Button>
        </ModalTrigger>

        <ModalContainer variant="popup">
          <ModalNavigation variant="normal">
            Normal
          </ModalNavigation>

          <ModalContent>
            <ModalContentItem>
              <ModalHeading>Heading</ModalHeading>
              <ModalSummary>Summary</ModalSummary>
              <ModalDescription>Description</ModalDescription>
            </ModalContentItem>
          </ModalContent>

          <ActionArea>
            <ActionAreaButton>
              Action
            </ActionAreaButton>
          </ActionArea>
        </ModalContainer>
      </Modal>

      <Modal>
        <ModalTrigger>
          <Button>Emphasized</Button>
        </ModalTrigger>

        <ModalContainer variant="popup">
          <ModalNavigation variant="emphasized">
            Emphasized
          </ModalNavigation>

          <ModalContent>
            <ModalContentItem>
              <ModalHeading>Heading</ModalHeading>
              <ModalSummary>Summary</ModalSummary>
              <ModalDescription>Description</ModalDescription>
            </ModalContentItem>
          </ModalContent>

          <ActionArea>
            <ActionAreaButton>
              Action
            </ActionAreaButton>
          </ActionArea>
        </ModalContainer>
      </Modal>

      <Modal>
        <ModalTrigger>
          <Button>Floating</Button>
        </ModalTrigger>

        <ModalContainer variant="popup">
          <ModalNavigation variant="floating" />

          <ModalContent>
            <ModalContentItem>
              <ModalHeading>Heading</ModalHeading>
              <ModalSummary>Summary</ModalSummary>
              <ModalDescription>Description</ModalDescription>
            </ModalContentItem>
          </ModalContent>

          <ActionArea>
            <ActionAreaButton>
              Action
            </ActionAreaButton>
          </ActionArea>
        </ModalContainer>
      </Modal>

      <Modal>
        <ModalTrigger>
          <Button>Display</Button>
        </ModalTrigger>

        <ModalContainer variant="popup">
          <ModalNavigation variant="display">
            Display
          </ModalNavigation>

          <ModalContent>
            <ModalContentItem>
              <ModalHeading>Heading</ModalHeading>
              <ModalSummary>Summary</ModalSummary>
              <ModalDescription>Description</ModalDescription>
            </ModalContentItem>
          </ModalContent>

          <ActionArea>
            <ActionAreaButton>
              Action
            </ActionAreaButton>
          </ActionArea>
        </ModalContainer>
      </Modal>
    </FlexBox>
  )
}

export default Demo;

Close button

닫기 버튼을 없애거나 좌측에 배치할 수도 있습니다.

import {
  Button,
  Modal,
  ModalTrigger,
  ActionArea,
  ActionAreaButton,
  ModalContainer,
  ModalContent,
  ModalContentItem,
  ModalDescription,
  ModalHeading,
  ModalNavigation,
  ModalSummary,
  ModalClose,
} from '@wanteddev/wds';

const Demo = () => {

  return (
    <Modal>
       <ModalTrigger>
        <Button>Open</Button>
      </ModalTrigger>

      <ModalContainer variant="popup">
        <ModalNavigation trailingContent={null} leadingContent={<ModalClose />}>
          Title
        </ModalNavigation>

        <ModalContent>
          <ModalContentItem>
            <ModalHeading>Heading</ModalHeading>
            <ModalSummary>Summary</ModalSummary>
            <ModalDescription>Description</ModalDescription>
          </ModalContentItem>
        </ModalContent>

        <ActionArea>
          <ActionAreaButton>
            Action
          </ActionAreaButton>
        </ActionArea>
      </ModalContainer>
    </Modal>
  )
}

export default Demo;

Customize

ModalContainer의 wrapperProps 를 사용하면 모달이 나타나는 위치를 조정할 수 있습니다.

import {
  Button,
  Modal,
  ModalTrigger,
  ActionArea,
  ActionAreaButton,
  ModalContainer,
  ModalContent,
  ModalContentItem,
  ModalDescription,
  ModalHeading,
  ModalNavigation,
  ModalSummary,
} from '@wanteddev/wds';

const Demo = () => {
  return (
    <Modal>
      <ModalTrigger>
        <Button>Open</Button>
      </ModalTrigger>

      <ModalContainer
        variant="popup"
        wrapperProps={{
          sx: {
            justifyContent: 'flex-end',
            alignItems: 'flex-end'
          }
        }}>
        <ModalNavigation>
          Title
        </ModalNavigation>

        <ModalContent>
          <ModalContentItem>
            <ModalHeading>Heading</ModalHeading>
            <ModalSummary>Summary</ModalSummary>
            <ModalDescription>Description</ModalDescription>
          </ModalContentItem>
        </ModalContent>

        <ActionArea>
          <ActionAreaButton>
            Action
          </ActionAreaButton>
        </ActionArea>
      </ModalContainer>
    </Modal>
  )
}

export default Demo;

With form

Form 구조로 사용하려면 ModalContainer 하위에 <FlexBox as="form" flex="1" flexDirection="column"> 구조를 사용합니다.

import {
  Button,
  Modal,
  ActionArea,
  ActionAreaButton,
  ModalContainer,
  ModalContent,
  ModalContentItem,
  ModalNavigation,
  ModalTrigger,
  TextField,
  FlexBox,
  FormField,
  FormControl,
  FormLabel,
  FormErrorMessage,
  FormMessage,
} from '@wanteddev/wds';
import { useForm, Controller } from 'react-hook-form';

const Demo = () => {
  const form = useForm({ defaultValues: { value: '' } });

  return (
    <Modal>
      <ModalTrigger>
        <Button>Open</Button>
      </ModalTrigger>

      <ModalContainer variant="popup">
        <FlexBox
          as="form"
          flex="1"
          flexDirection="column"
          onSubmit={form.handleSubmit((v) => alert(JSON.stringify(v)))}
        >
          <ModalNavigation>
            Title
          </ModalNavigation>

          <ModalContent>
            <ModalContentItem>
              <Controller
                control={form.control}
                name="value"
                rules={{
                  required: {
                    value: true,
                    message: '필수 값입니다.'
                  }
                }}
                render={({ field, formState }) => (
                  <FormField>
                    <FormLabel>라벨</FormLabel>

                    <FormControl>
                      <TextField {...field} width="100%" placeholder="값을 입력하세요." invalid={Boolean(formState.errors.test)} />
                    </FormControl>

                    {Boolean(formState.errors.test) ? (
                      <FormErrorMessage>{formState.errors.test?.message}</FormErrorMessage>
                    ): (
                      <FormMessage>Description</FormMessage>
                    )}
                  </FormField>
                )} />
            </ModalContentItem>
          </ModalContent>

          <ActionArea>
            <ActionAreaButton type="submit">
              Submit
            </ActionAreaButton>
          </ActionArea>
        </FlexBox>
      </ModalContainer>
    </Modal>
  )
}

export default Demo;

API

NameTypesdefaultValue
open
boolean
-
defaultOpen
boolean
-
onOpenChange
(open: boolean) => void
-
onVisibilityChange
(visibility: "hidden" | "visible") => void
-
children
ReactNode
-
sx
SxProp
-

ModalContainer

NameTypesdefaultValue
as
ElementType
-
variant
"bottom" | "popup" | "full"
"popup"
handle
boolean
-
peekHeight
number
-
sticky
boolean
true
size
"small" | "medium" | "large" | "xlarge"
"medium"
resize
"fixed" | "hug"
"hug"
children
ReactNode
-
wrapperProps
DefaultComponentProps<{}, 'div'>
-
dimmer
ReactNode
<ModalDimmer />
container
null | Element | DocumentFragment
-
disableOutsideClickClose
boolean
false
disableEscapeKeyDownClose
boolean
false
disableRemoveScroll
boolean
false
disableFocusScope
boolean
false
disableAriaHiddenOthers
boolean
false
disablePortal
boolean
false
forceMount
boolean
false
sx
SxProp
-
xl
Merge<Pick<ModalContainerDefaultProps, "variant" | "size" | "handle" | "resize">, { sx?: CSSInterpolation; }> | undefined
-
lg
Merge<Pick<ModalContainerDefaultProps, "variant" | "size" | "handle" | "resize">, { sx?: CSSInterpolation; }> | undefined
-
md
Merge<Pick<ModalContainerDefaultProps, "variant" | "size" | "handle" | "resize">, { sx?: CSSInterpolation; }> | undefined
-
sm
Merge<Pick<ModalContainerDefaultProps, "variant" | "size" | "handle" | "resize">, { sx?: CSSInterpolation; }> | undefined
-
xs
Merge<Pick<ModalContainerDefaultProps, "variant" | "size" | "handle" | "resize">, { sx?: CSSInterpolation; }> | undefined
-

ModalNavigation

NameTypesdefaultValue
variant
"search" | "display" | "normal" | "floating" | "emphasized"
-
leadingContent
ReactNode
-
trailingContent
ReactNode
<ModalClose />
children
ReactNode
-
sx
SxProp
-
xl
{ sx?: CSSInterpolation; } | undefined
-
lg
{ sx?: CSSInterpolation; } | undefined
-
md
{ sx?: CSSInterpolation; } | undefined
-
sm
{ sx?: CSSInterpolation; } | undefined
-
xs
{ sx?: CSSInterpolation; } | undefined
-
background
boolean
-
toolbar
ReactNode
-
titleId
string
-

ModalNavigationButton

TopNavigationButton 과 동일한 Props를 사용합니다.

NameTypesdefaultValue
as
E
-
variant
"text" | "icon"
-
color
"primary" | "assistive"
-
disabled
boolean
-
size
number | "small" | "medium"
-
children
ReactNode
-
sx
SxProp
-

ModalClose

TopNavigationButton 과 동일한 Props를 사용합니다.

NameTypesdefaultValue
as
E
-
variant
"text" | "icon"
-
color
"primary" | "assistive"
-
disabled
boolean
-
size
number | "small" | "medium"
-
children
ReactNode
-
sx
SxProp
-

ModalContent

NameTypesdefaultValue
gap
Property.Gap<string | number> | undefined
"calc(var(--wds-modal-content-margin, 20px))"
children
ReactNode
-
sx
SxProp
-
xl
Merge<Pick<ModalContentDefaultProps, "gap">, { sx?: CSSInterpolation; }> | undefined
-
lg
Merge<Pick<ModalContentDefaultProps, "gap">, { sx?: CSSInterpolation; }> | undefined
-
md
Merge<Pick<ModalContentDefaultProps, "gap">, { sx?: CSSInterpolation; }> | undefined
-
sm
Merge<Pick<ModalContentDefaultProps, "gap">, { sx?: CSSInterpolation; }> | undefined
-
xs
Merge<Pick<ModalContentDefaultProps, "gap">, { sx?: CSSInterpolation; }> | undefined
-

ModalContentItem

FlexBox 와 동일한 Props를 사용합니다.

ModalHeading

Typography 와 동일한 Props를 사용합니다.

ModalSummary

Typography 와 동일한 Props를 사용합니다.

ModalDescription

Typography 와 동일한 Props를 사용합니다.

© 2026 Wanted Lab, Inc.