Skip to content

Modal

Modal dialog that displays content in an overlay, guiding users through related actions.

Basic Usage

Control visibility with the visible prop and onClose callback.

tsx
import { Modal, Button } from '@airo-ui/react'
import { useState } from 'react'

function Demo() {
  const [visible, setVisible] = useState(false)

  return (
    <div>
      <Button variant="primary" onClick={() => setVisible(true)}>
        Open Modal
      </Button>
      <Modal
        visible={visible}
        title="Notification"
        onClose={() => setVisible(false)}
        onConfirm={() => {
          console.log('confirmed')
          setVisible(false)
        }}
      >
        This is the modal content area.
      </Modal>
    </div>
  )
}

Custom Width

tsx
<Modal visible={visible} title="Wide Modal" width="800px" onClose={onClose}>
  This modal is 800px wide.
</Modal>

Hide the footer button area by not providing onConfirm or cancelText.

tsx
<Modal visible={visible} title="No Footer" onClose={onClose}>
  This modal has no footer buttons.
</Modal>

Confirm Loading

tsx
import { useState } from 'react'

function Demo() {
  const [visible, setVisible] = useState(false)
  const [confirmLoading, setConfirmLoading] = useState(false)

  const handleConfirm = () => {
    setConfirmLoading(true)
    setTimeout(() => {
      setConfirmLoading(false)
      setVisible(false)
    }, 2000)
  }

  return (
    <Modal
      visible={visible}
      title="Confirm Action"
      confirmLoading={confirmLoading}
      onConfirm={handleConfirm}
      onClose={() => setVisible(false)}
    >
      Are you sure? After confirming, there will be a 2s loading state.
    </Modal>
  )
}
tsx
<Modal
  visible={visible}
  title="Custom Footer"
  footer={
    <div style={{ display: 'flex', gap: 8, justifyContent: 'flex-end' }}>
      <Button variant="ghost" onClick={() => setVisible(false)}>Later</Button>
      <Button variant="primary" onClick={() => setVisible(false)}>Got it</Button>
    </div>
  }
  onClose={() => setVisible(false)}
>
  <p>The modal content area can hold any content.</p>
</Modal>

API

Props

NameTypeDefaultDescription
visiblebooleanfalseVisibility
titlestring--Title
widthstring'520px'Modal width
closablebooleantrueShow close button, support Esc
maskClosablebooleantrueClose on mask click
destroyOnClosebooleanfalseDestroy content on close
confirmTextstring'Confirm'Confirm button text
cancelTextstring'Cancel'Cancel button text
confirmLoadingbooleanfalseConfirm button loading
childrenReact.ReactNode--Modal body content
footerReact.ReactNode--Custom footer (overrides default buttons)
onOpen() => void--Modal opened callback
onClose() => void--Modal closed callback
onConfirm() => void--Confirm button click handler
onCancel() => void--Cancel button click handler

Interaction

  • Opens with panel animating from scale(0.92) translateY(12px) to normal state with spring curve
  • Closes with panel scaling to scale(0.96) and mask fading out
  • Mask click closes (disable via maskClosable)
  • Esc key closes (disable via closable)
  • Body scroll locked on open, restored on close

MIT License