Modal is installed into your project as a file you own. It takes the same props as Ant Design's Modal and adds no variants or styling of its own. The one difference is that className goes through cn(), so conflicting Tailwind classes in your string resolve the way they do in every other component.
Install
npx antuikit@latest init
npx antuikit@latest add modal
The first command installs AntuikitProvider and the theme tokens, and the second adds Modal.
Import
Import it from @/components/ui/modal, not from antd. Once installed, the file is yours, so the path points into your project.
import { Modal } from "@/components/ui/modal"
Static methods and App.useApp()
Modal does not re-export the static methods: Modal.confirm, info, success, error, warning, useModal and destroyAll. Imported straight from antd, the calls render outside your provider and come out unthemed, with a console warning.
Take the same calls from App.useApp() instead. It needs no setup, because AntuikitProvider already renders the <App> these dialogs read their theme from.
1
import { App } from "antd";
2
3
function DeleteButton() {
4
const { modal } = App.useApp();
5
return (
6
<Button
7
onClick={() =>
8
modal.confirm({
9
title: "Delete this project?",
10
content: "This cannot be undone.",
11
okButtonProps: { danger: true },
12
})
13
}
14
>
15
Delete
16
</Button>
17
);
18
}
Modal.useModal() is themed too, if you would rather hold the context yourself. It returns the calls plus a contextHolder, and the dialogs only appear once you render it. App and Modal.useModal are among the few things you import from antd directly.
Variants
Modal has no variant props of its own. What changes from one dialog to the next is its size, its footer and its content, and you set all three with values rather than a variant name. The one enumerated prop in the table is okType, the Button type of the OK button.
Flags
Eight booleans. keyboard and scrollLock default to true, and turning either off costs accessibility.
open shows the dialog, and you control it. The dialog never closes itself: every way out calls onCancel, which has to set it back.
confirmLoading puts a spinner on OK. While it is set, Escape, the mask and Cancel do nothing.
loading is the other wait: it shows a skeleton in place of the body and removes the footer, for a dialog that opens before its content has arrived.
centered places the dialog in the middle of the viewport instead of near the top.
destroyOnHidden unmounts the content once the close animation ends. Without it a half-filled form is still half-filled the next time the dialog opens.
forceRender mounts the content before the first open, and wins over destroyOnHidden when both are set.
keyboard (true) is the Escape key. Only the topmost dialog in a stack receives it.
scrollLock (true) locks the page behind the mask. Turning it off lets people scroll a page they cannot touch.
Two more booleans sit inside objects: mask.closable (true) is the click-outside exit, and focusable.trap defaults to whether a mask is rendered at all.
Slots
The props that take a node. Reach for these before a descendant selector: the dialog renders in a portal, so a selector written on a parent never reaches it.
title is the header, and also the dialog's accessible name: aria-labelledby points at it, and an untitled dialog has no name. Pass one even where the design shows no heading, hidden with sr-only.
children is the body.
footer takes a node to replace the row, null to remove it, or a function (originNode, { OkBtn, CancelBtn }) to rearrange the stock buttons.
okText and cancelText come from the active locale when unset. Put the verb in okText rather than leaving “OK”.
closeIcon replaces the glyph in the close button. Modal draws antd's own close icon, not your icon pack's, so pass the pack's glyph here.
modalRender wraps the whole dialog (title, body and footer together) in an element of your own, like a <form> or a draggable shell.
className lands on the dialog box, rootClassName on the portal root and wrapClassName on the scroll wrapper.
Events
onOk: called when the OK button is clicked. It does not close the dialog.
onCancel: called for every other way out, the close button, Cancel, a click on the mask and Escape.
afterClose: called once the close animation has finished.
afterOpenChange: called when the open or the close animation ends, with the new state.
closable: its object form takes an onClose and an afterClose of its own.
Styling
classNames and styles take the same part keys. Reach for these before a descendant selector: a part with a key stays stable across Ant Design releases.
1
<Modal
2
classNames={{
3
root: "…",
4
header: "…",
5
body: "…",
6
footer: "…",
7
container: "…",
8
title: "…",
9
wrapper: "…",
10
mask: "…",
11
close: "…",
12
}}
13
/>
A Tailwind class on one of these keys wins over the default styles without !important. Writing a component token's CSS variable on the root does nothing, so style the part, or set the token through ConfigProvider.
API Reference
Modal
Modal's props.
Modal.method()
The config confirm, info, success, error and warning take, from App.useApp() or Modal.useModal().
Modal.useModal()
What Modal.useModal() returns, as [modal, contextHolder]. App.useApp() returns the same modal.