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>No Footer
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>
)
}Custom Footer
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
| Name | Type | Default | Description |
|---|---|---|---|
visible | boolean | false | Visibility |
title | string | -- | Title |
width | string | '520px' | Modal width |
closable | boolean | true | Show close button, support Esc |
maskClosable | boolean | true | Close on mask click |
destroyOnClose | boolean | false | Destroy content on close |
confirmText | string | 'Confirm' | Confirm button text |
cancelText | string | 'Cancel' | Cancel button text |
confirmLoading | boolean | false | Confirm button loading |
children | React.ReactNode | -- | Modal body content |
footer | React.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) Esckey closes (disable viaclosable)- Body scroll locked on open, restored on close