Siteplane UI

Modal

Pro

Ready-made modals for confirmations, forms, sharing, payments and more.

Markdown
Pro

This example installs with a Siteplane UI Pro license key.

Get a license on the Pro page.

pnpm dlx shadcn@4.18.0 add @siteplane-pro/modal-block-demo

This Pattern is part of Siteplane UI Pro, and its source comes with a Pro license. Its previews are free to view and try.

Installation

pnpm dlx shadcn@4.18.0 add @siteplane-pro/modal-block
Pro

Installs with a Siteplane UI Pro license key. Complete the one-time Pro setup first.

Get a license on the Pro page.

Usage

import { ModalBlock } from "@/siteplane/components/modal-block";

<ModalBlock
  trigger="Delete post"
  title="Delete post"
  description="Are you sure? This action cannot be undone."
  tone="error"
  confirmLabel="Delete"
  onConfirm={() => console.log("Post deleted")}
/>;

ModalBlock renders the Button that opens it, the popup with its header and actions, and the close button. Put fields and other content in children, and set onSubmit to turn the dialog into a form. To open the dialog from your own control, such as a menu item, pass trigger={null} and control it with open and onOpenChange.

Composition

ModalBlock
├── aside (optional)
├── media (optional)
├── header
│   ├── icon
│   ├── title
│   └── description
├── children
└── actions
    ├── footerStart (optional)
    └── Cancel and Confirm, or footer

Place these content parts in children, or use any other public component:

  • Fields: ModalTextField, ModalTextarea, ModalPasswordField, ModalSelect, ModalSearchField, ModalTags, ModalChoices, ModalTimeField and ModalVerification, framed by ModalFieldFrame and ModalFieldError.
  • Sign in: ModalAuthFields.
  • Sharing and people: ModalShare, ModalLinkField, ModalInviteEmails, ModalMemberPicker and ModalPeople.
  • Payment: ModalPaymentFields, ModalPlanPicker, ModalPaymentMethods and ModalSeats.
  • Dates: ModalDateRangeFields, ModalEventFields and ModalEventDetails.
  • Media and editing: ModalMediaCarousel, ModalImageCrop, ModalUpload and ModalTextEditor.
  • Ready-made groups: ModalProfileFields, ModalProjectFields, ModalCompanyFields, ModalExperienceFields, ModalUserSettings, ModalAppearance, ModalBannerAppearance, ModalChecklist, ModalAssistant and ModalMessage.

Some parts have a fixed place. ModalCardPreview and ModalTeamAvatars fill media, ModalConnection fills icon, and ModalCopyLink and ModalDateRangeInputs fill footerStart. With appearance="button", ModalCopyLink is a Button like Cancel and goes in footer with the other actions.

Confirmations and alerts

tone colors the icon as success, warning or error, and an error tone also renders the confirm button in the destructive style. A confirmation focuses Cancel when it opens, so pressing Enter never confirms by accident. layout="horizontal" puts the icon beside the text, and footerStart adds an option such as “Don’t show again” at the start of the action row.

Pro

This example installs with a Siteplane UI Pro license key.

Get a license on the Pro page.

pnpm dlx shadcn@4.18.0 add @siteplane-pro/modal-block-destructive-example

When the Cancel slot carries a risky label such as Discard, set initialFocus="confirm" so Enter keeps the safe choice.

Pro

This example installs with a Siteplane UI Pro license key.

Get a license on the Pro page.

pnpm dlx shadcn@4.18.0 add @siteplane-pro/modal-block-unsaved-changes-example

Mark the actions of a custom footer with data-modal-action="cancel" and data-modal-action="confirm" so that the dialog finds them. Without the marks, a confirmation takes the first footer button that is not an accent, destructive or inverse Button, in any of their variants, as its Cancel, so it never opens on its main or destructive action.

For a request from a person, such as a request for access, show them with ModalPeople in the content.

Forms and settings

Pass onSubmit to make the dialog a form. The content sits in a Form, the confirm button submits it and Enter in a field submits it. Fields are validated first and show their error inline; onSubmit receives the FormData once every field is valid. The dialog stays open until you close it, so close it after your request succeeds.

Pro

This example installs with a Siteplane UI Pro license key.

Get a license on the Pro page.

pnpm dlx shadcn@4.18.0 add @siteplane-pro/modal-block-form-example

Set dividers to read a long form as three bands: header, fields and actions. Fields placed in footerStart belong to the form too, so their values arrive in the same FormData.

Pro

This example installs with a Siteplane UI Pro license key.

Get a license on the Pro page.

pnpm dlx shadcn@4.18.0 add @siteplane-pro/modal-block-settings-example

The ready-made field groups ModalExperienceFields, ModalCompanyFields, ModalProfileFields, ModalProjectFields and ModalUserSettings cover common profile and project forms. ModalChoices lists checkboxes, switches or radio options, optionally as cards. Its defaultValue takes an id or a list of ids in every mode. A radio group starts on the first id, or on the first option when there is none. For a checkbox or a switch, an option's own checked decides for that option when it is set. ModalTags collects labels, ModalSeats a seat count with its price and ModalTextEditor formatted text. The editor's tools choose the tools of its toolbar, such as ["bold", "italic", "bulletList", "link"] for a short note; the toolbar keeps its own order, and without tools it shows all of them.

Pending and failed requests

onConfirm and onSubmit may return a promise. Until it settles, the confirm button shows its spinner and ignores a second press, and Enter in a form field submits nothing. Cancel and Escape still close the dialog. A dialog without onSubmit closes once the promise of onConfirm resolves and stays open when it rejects. A form dialog stays open either way, so close it through open after the request succeeds. When your application tracks the request itself, set confirmLoading instead.

Show a failure with confirmError. The message appears under the content as an alert, so screen readers announce it, and it opens and closes with motion. Clear it when the person tries again.

const [error, setError] = useState<string>();

<ModalBlock
  trigger="Delete project"
  title="Delete project"
  tone="error"
  confirmLabel="Delete"
  confirmError={error}
  onConfirm={async () => {
    setError(undefined);
    try {
      await deleteProject();
    } catch (cause) {
      setError("The project could not be deleted. Try again.");
      // A rejected promise keeps the dialog open.
      throw cause;
    }
  }}
/>;

The second argument of onOpenChange is a ModalBlockChangeDetails object. Its reason says why the dialog changes, such as escape-key, outside-press or close-press, and eventDetails.cancel() keeps the dialog as it is, for example until a request has finished. The argument is missing when the confirm button closes the dialog.

Sharing and invites

ModalShare combines a copyable link with a permission menu and a searchable list of people. Use ModalInviteEmails for a list of addresses, ModalMemberPicker to add people from a list, and ModalLinkField or ModalCopyLink for a link on its own. ModalTeamAvatars works well as media when welcoming new members.

Pro

This example installs with a Siteplane UI Pro license key.

Get a license on the Pro page.

pnpm dlx shadcn@4.18.0 add @siteplane-pro/modal-block-share-example

Sign in and verification

A dialog that brings its own submit button sets hideFooter. ModalAuthFields renders a login or sign-up form that fits the xs width, with an optional forgot-password link and further actions such as single sign-on. mode switches between login, signup and signup-social.

Pro

This example installs with a Siteplane UI Pro license key.

Get a license on the Pro page.

pnpm dlx shadcn@4.18.0 add @siteplane-pro/modal-block-sign-in-example

ModalVerification renders a one-time code with a field for each digit. layout="centered" gathers it under a centered header; layout="stacked" fits a left-aligned header and spreads six cells over the full width. Put a resend link in description. ModalPasswordField adds a masked field with a show toggle for a password prompt.

Pro

This example installs with a Siteplane UI Pro license key.

Get a license on the Pro page.

pnpm dlx shadcn@4.18.0 add @siteplane-pro/modal-block-verification-example

Payment and plans

ModalPaymentFields formats the card number and expiry while people type and reports every change through onValueChange, so a ModalCardPreview in media can mirror the card. With the card at the top, the close button is off by default; Cancel, Escape and a click outside close the dialog. ModalPlanPicker (compact or rich), ModalPaymentMethods and ModalSeats cover plan changes, saved cards and seat counts.

Pro

This example installs with a Siteplane UI Pro license key.

Get a license on the Pro page.

pnpm dlx shadcn@4.18.0 add @siteplane-pro/modal-block-payment-example

Media

ModalMediaCarousel pages through slides with a caption, dots and Back, Next and Skip actions. A slide can be a video: it rests on its poster behind a Play button. The carousel brings its own actions, so the dialog sets hideFooter, and as its pictures start the content, showCloseButton={false} lets them begin 24 px from the top edge. Keep a visible way to close the dialog, here Skip and the last slide’s button. ModalImageCrop adds a movable crop frame over a choice of images. The whole picture shows inside its 16:9 area, so a tall photo can be framed from top to bottom. The selected thumbnail shows what the frame holds. The crop value is measured in the picture, from 0 to 1 across its width and height, and onImageLoad reports the picture’s size in pixels.

Pro

This example installs with a Siteplane UI Pro license key.

Get a license on the Pro page.

pnpm dlx shadcn@4.18.0 add @siteplane-pro/modal-block-carousel-example

A single picture goes in media. It starts 24 px from the top edge and keeps its own 24 px from the sides, as in this example. With media the close button is off by default, as Cancel, Escape and a click outside close the dialog. Set showCloseButton to show it: it sits over the top-end corner of the picture as an opaque button that reads on any image.

Pro

This example installs with a Siteplane UI Pro license key.

Get a license on the Pro page.

pnpm dlx shadcn@4.18.0 add @siteplane-pro/modal-block-media-example

Dates

Combine dividers and width="lg" for a range picker. In the content, ModalDateRangeFields with twoMonths shows two months from the lg breakpoint up, and its presets are the Calendar's own shortcuts above them. showFields={false} leaves out its date fields, because ModalDateRangeInputs in footerStart puts the range next to the actions: a Date Range Picker without its calendar button, since the calendar in the dialog is the picker. ModalEventFields adds a calendar with shortcuts, times and a location for creating an event, and ModalEventDetails shows an invitation.

Pro

This example installs with a Siteplane UI Pro license key.

Get a license on the Pro page.

pnpm dlx shadcn@4.18.0 add @siteplane-pro/modal-block-date-range-example

Uploads

ModalUpload wraps File Upload. Your application owns the files state and starts the transfer in onFilesSelected. Set avatar to show a picture beside the dropzone, as in a profile dialog, and compact for the row of a labeled form. ModalUpload sets the hint in the dropzone through its own description, “Choose files or drop them here.” by default. Its labels reach the File Upload inside, for the upload prompt, the file states, the actions and the rejection messages. Each word left out keeps File Upload's English default.

Pro

This example installs with a Siteplane UI Pro license key.

Get a license on the Pro page.

pnpm dlx shadcn@4.18.0 add @siteplane-pro/modal-block-upload-example

Assistants, messages and integrations

ModalAssistant fills a centered dialog with prompt suggestions and a composer, and ModalMessage is a complete compose form with recipients, subject, text and attachments. For an integration, pass ModalConnection as the icon and list what it may access with ModalChecklist. These parts take their callbacks as props, so connect them to your own actions.

API reference

ModalBlock

Component properties
PropTypeDefaultDescription
titleReactNoderequiredDialog title and accessible name. With hideHeader it stays available to assistive technology.
descriptionReactNodeunsetText under the title; also the accessible description.
iconReactNodeunsetDecorative icon above the title, or beside it in the horizontal layout. tone colors it.
childrenReactNodeunsetContent between the header and the actions, in a scrollable panel with 16 px between children.
layoutstacked | horizontal | centeredstackedHeader arrangement: icon above a left-aligned title, icon beside the text, or everything centered.
toneneutral | success | warning | errorneutralColors the icon. error also renders the confirm button in the destructive style. The earlier names default and destructive still work as deprecated aliases of neutral and error.
widthxs | sm | md | lgsmPopup width from the sm breakpoint up: 400, 440, 600 and 880 px. Smaller screens show a full-width bottom sheet at every step.
triggerReactNodeOpen dialogContent of the Button that opens the dialog. null or false renders no Button, for a dialog that your application opens through open.
confirmLabelReactNodeConfirmText of the primary action; an icon may lead it.
cancelLabelReactNodeCancelText of the secondary action, which closes the dialog; an icon may lead it.
footerStartReactNodeunsetContent at the start of the action row, such as a checkbox, a link or a secondary Button. In a form dialog it belongs to the form.
footerReactNodeunsetReplaces the Cancel and Confirm buttons. Close the dialog from your own actions through open and onOpenChange, and mark them with data-modal-action="cancel" and data-modal-action="confirm" for the initial focus.
mediaReactNodeunsetPicture or illustration above the header, 24 px from the top edge; it keeps its own 24 px from the sides. Turns the close button off by default.
openbooleanuncontrolledControlled open state.
defaultOpenbooleanfalseInitial open state when uncontrolled.
onOpenChange(open: boolean, eventDetails?: ModalBlockChangeDetails) => voidunsetCalled when the dialog asks to open or close. eventDetails gives the reason, such as escape-key or close-press, and its cancel() keeps the dialog as it is. It is missing when the confirm button closes the dialog.
onSubmit(data: FormData) => void | Promise<unknown>unsetMakes the dialog a form. The content sits in a Form, the confirm button submits it and Enter in a field submits it. Called with the form data once every field is valid. A returned promise keeps the confirm button loading until it settles, and the dialog stays open until you close it. Without onSubmit there is no Form.
onConfirm() => void | Promise<unknown>unsetCalled when the confirm button is pressed in a dialog without onSubmit. The dialog then closes, or, for a returned promise, closes once it resolves and stays open when it rejects.
confirmLoadingbooleanfalseShows the spinner of the confirm button and blocks it, for a request that your application tracks itself.
confirmErrorReactNodeunsetMessage under the content when the confirm action did not work. It is an alert, so screen readers announce it. Clear it when the person tries again.
hideFooterbooleanfalseOmits the action row, for content that brings its own actions.
actionLayoutfill | endfill or endfill lets the buttons share the row; end aligns them to the end at their own width. The default is fill for the xs and sm widths unless the layout is horizontal, and end otherwise. Links keep their own width, and so do the buttons beside a footerStart, so fill has no effect there.
hideHeaderbooleanfalseHides the header visually. Title and description stay available to assistive technology.
asideReactNodeunsetA rail at the start of the dialog, as tall as the dialog and divided by a vertical line, such as the presets of a date range. On small screens it sits above the content with a horizontal line.
closeLabelstringCloseAccessible name of the close button.
showCloseButtonbooleantrue, false with mediaShows the close button. Without media it sits in the top-end corner of the dialog; with media it sits over the top-end corner of the media as an opaque button. Escape and a click on the backdrop always close the dialog, so keep a visible close action without it.
dividersbooleanfalseDraws a full-width divider under the header and another above the actions. A divider only appears between two parts that are shown.
initialFocusauto | cancel | confirmautoControl that takes focus on open. auto picks the first empty text field, then Cancel in a confirmation, then the first field or choice, then the primary action, which a confirmation replaces with the dialog itself. cancel without a safe action also focuses the dialog. Set confirm when Cancel carries a risky label such as Discard.

ModalBlock composes Dialog, Form, Button and Separator, so it inherits their focus handling, motion and portal. The types ModalBlockProps, ModalBlockChangeDetails, the second argument of onOpenChange, and ModalBlockTone, with ModalBlockDeprecatedTone for the earlier names, are exported with the component.

Parts

In this table, Prop is the part, Type lists the props it requires and the ones you need most, Default a notable default and Description what the part is for. The installed source has the complete props.

Component properties
PropTypeDefaultDescription
ModalTextFieldlabel, hint, icon—Labeled text input.
ModalTextarealabel, help, layout—Labeled textarea.
ModalPasswordFieldlabel, icon, labels—Masked field with toggle.
ModalSelectlabel, name, options, icon—Labeled select.
ModalSelectOptionLabelicon, children—Option with icon.
ModalSearchFieldlabel, name, items—Searchable field.
ModalChoicesoptions, label, mode, defaultValuemode: checkboxChoice list.
ModalTagslabel, options, layout—Multi-value chip field.
ModalTimeFieldlabel, name, defaultValue, labels—Time of day field.
ModalVerificationlabel, lengthlength: 6One-time code input.
ModalFieldFramelabel, htmlFor—Frame for your own control.
ModalFieldErrorlabel, minLength—Inline validation message.
ModalAuthFieldsmode, labelsmode: loginLogin or sign-up.
ModalSharepeople, link, permissionOptions, searchLabel, linkLabel, copyLabel, copiedLabel, failedLabel, accessLabel, accessHint, permissionLabel—Sharing panel.
ModalLinkFieldlabel, value—Read-only link with copy.
ModalCopyLinkvalue, appearanceappearance: linkCopy action.
ModalInviteEmailslabel, addLabel—Email address list.
ModalMemberPickerpeople, defaultSelected—Pick people.
ModalPeoplepeople, onRemove—People list with roles.
ModalTeamAvatarspeople—Overlapping avatars.
ModalPaymentFieldsdefaultValue, onValueChange—Card fields.
ModalCardPreviewname, number, expiry, brand—Card preview.
ModalPlanPickerplans, richrich: falsePlan choice list.
ModalPaymentMethodsmethods, onEdit, onSetDefault—Card list.
ModalSeatsprice, formatPrice—Seat count with total.
ModalDateRangeFieldsvalue, onValueChange, presets—Range calendar; presets show as its shortcuts.
ModalDateRangePresetspresets, onValueChange—Deprecated preset buttons; pass presets to ModalDateRangeFields. Works until 2.0.
ModalDateRangeInputsvalue, onValueChange, label, startLabel, endLabellabel: Date rangeFooter range: a Date Range Picker without its calendar button.
ModalEventFieldsrange, onRangeChange, presets, labels—Create-event fields.
ModalEventDetailsdate, time, reminder, organizer, attendees, status, onAddGuest, labels—Invitation details.
ModalMediaCarouselslides, onFinish, onSkip—Slide carousel.
ModalImageCropimages, value, onValueChange—Image crop frame.
ModalUploadfiles, onFilesSelected, labels—Dropzone with files.
ModalTextEditorlabel, name, defaultText, onValueChange, tools, labels—Rich text field.
ModalProfileFieldsupload, termsHref—Profile fields.
ModalProjectFieldsupload, teams, tags—Project fields.
ModalCompanyFieldsupload, usernamePrefix, labels—Company fields.
ModalExperienceFieldscompanies, locations, employment, labels—Work experience.
ModalUserSettingsuser, countries, stats, banner, onArchive, onEdit, labels—Profile settings.
ModalAppearancedefaultColor, onColorChange, labels—Color and theme.
ModalAppearancePreviewmode, banner—App illustration.
ModalBannerAppearanceoptions, onEditCss—Banner choices.
ModalConnectionfrom, to—Icon for two linked apps.
ModalChecklisttitle, items—Heading with checked list.
ModalAssistantprompts, user, users, onPromptSelect, onMore, onDictate, onAttach, onShortcuts, labels—Prompts and composer.
ModalMessagerecipients, sender, defaultTo, onAttach, onRemoveAttachment, onDiscard, onSave, onSchedule, onSnippet, labels—Message composer.

ModalTextField, ModalTextarea and ModalPasswordField also pass native input attributes through. Parts that submit a value name it, so a form dialog collects the values in the FormData that onSubmit receives. The messages of fields read in English.

Parts with words of their own take them as props. The labels of ModalAppearance, ModalCompanyFields, ModalEventDetails, ModalEventFields, ModalExperienceFields, ModalUserSettings, ModalAssistant, ModalMessage, ModalTextEditor, ModalPasswordField, ModalTimeField and ModalUpload take any of their words and keep the English default for the others. ModalPasswordField names its show and hide toggle with show and hide, ModalTimeField passes its words to the time field and its selector, and ModalUpload passes File Upload's labels on. ModalAuthFields, ModalPaymentFields, ModalPaymentMethods, ModalMediaCarousel, ModalProfileFields and ModalProjectFields take a complete labels object. Other parts take single props, such as copyLabel and accessLabel on ModalShare or removeLabel on ModalPeople.

The types ModalPerson, ModalOption, ModalSelectOption, ModalFieldLayout, ModalPaymentValue, ModalPlan, ModalDateRange and ModalCrop are exported, as is ModalTextEditorTool, the name of a toolbar tool, and so are the label types, such as ModalMessageLabels, ModalPasswordFieldLabels and ModalTimeFieldLabels, which name every key; the installed source has the English defaults. A ModalDateRange is { from: Date; to: Date }, the range shape of Calendar. The date range parts, ModalImageCrop and ModalTextEditor report a change through onValueChange; their earlier onChange still works as a deprecated alias.

Usage notes

Every modal needs a title, even with hideHeader; the header then stays available to assistive technology. Icons passed as icon are decorative and hidden from assistive technology, so state the meaning in the title or the description as well.

On open, focus goes to the first empty text field. Without one, a confirmation, meaning a warning or error dialog or one with nothing to interact with, focuses Cancel. Without either, the first field or choice takes focus, and without that the primary action; a confirmation never opens on its primary action and focuses the dialog itself instead. Touch devices focus the popup itself so the on-screen keyboard stays closed. Use initialFocus to choose another target.

Escape, a click on the backdrop and Cancel close the dialog. Without the close button, after showCloseButton={false} or with media by default, keep a visible action that closes the dialog. For a decision that must be made before continuing, use Alert Dialog instead.

A form dialog never closes itself on submit, so keep it open while a request is pending or has failed and close it when it succeeds. Return a promise from onSubmit or onConfirm, or set confirmLoading, so the confirm button shows that the request is running, and show a failure with confirmError.

Default labels and the built-in validation messages are in English. The parts take most of their words as props, but a few words have no prop and stay in English. Among them are the corner names of the ModalImageCrop handles, the Edit CSS action of ModalBannerAppearance, the terms message of ModalProfileFields, the name of the help button beside a field label, the copy failure message of ModalLinkField and the carousel role description of ModalMediaCarousel, as well as the placeholders “MM/YY” in the expiry field of ModalPaymentFields and “name@example.com” in ModalInviteEmails. Kit components inside a part also keep their own English defaults where the part does not pass words on, such as the color controls of ModalAppearance. For another language, change these in the installed source. The date parts, ModalDateRangeFields, ModalDateRangeInputs and the calendar of ModalEventFields, show dates in the en-US format.

Below the sm breakpoint the dialog is a full-width bottom sheet at every width, and an aside rail moves above the content. Long content scrolls inside the panel while the header and actions stay in view.

To open a modal from a menu item or another control, pass trigger={null} and control the dialog with open and onOpenChange.

ModalMediaCarousel, ModalImageCrop and ModalTextEditor ship inside this pattern; they are not separate components.