Dialogs and feedback
Built-in dialogs require SubmitFlowProvider. The provider serializes standalone confirmation, acknowledgement, and action confirmation requests so only one built-in prompt is active at a time. Each request has an explicit result; dismissal never counts as confirmation.
Standalone confirmation
const feedback = useSubmitFeedback();
const confirmed = await feedback.confirm({
title: "Delete this project?",
description: "You cannot undo this action.",
destructive: true,
});
if (confirmed) await deleteProject();
confirm() returns true only when the user confirms. Cancel and allowed dismissal paths return false.
Acknowledgement
const result = await feedback.alert({
title: "Export ready",
description: "Your archive has been created.",
variant: "success",
acknowledgeLabel: "Done",
});
The result distinguishes acknowledgement from dismissal: { status: 'acknowledged' } or { status: 'dismissed', reason }.
Presentation and dismissal
Dialogs accept neutral, danger, warning, success, or info variants and responsive sm, md, or lg sizes. An optional width respects the viewport. content accepts React content for a summary or preview; provide a readable title and keep description concise.
Escape closes an undecided dialog by default. Backdrop presses are ignored unless outsidePress is enabled; they never confirm. closeOnEscape: false keeps the active prompt open. While an action runs in a retained dialog, Escape and backdrop presses cannot close or cancel that action; use its Cancel action to request cancellation.
Built-in dialogs use the browser's native <dialog> element. The current docs site does not claim tested focus restoration when nested inside another dialog library. If your app has its own modal stack, use an adapter that integrates with that stack and verify focus, Escape, and outside-click behavior there.
Dialogs with three actions
For choices such as Save / Discard / Cancel or Save draft / Publish / Cancel, render your own dialog with SubmitFlowRoot, two ActionButton controls, and a CancelButton. The two async actions share one flow lock, so starting either disables both action buttons and shows its pending label. Set feedback: 'inline' on each action and render SubmitFeedback inside the dialog to display progress and errors.
Close your dialog from each action's onSuccess callback. Leave it open on failure so the user can retry or choose the other action. While busy, CancelButton requests cancellation; while idle, its onCancel callback can close the dialog. In these examples, Escape closes only while idle, and the backdrop keeps the dialog open. The custom dialog owns its accessible title, focus management, and dismissal behavior.
Try the unsaved changes dialog and the release review dialog, including its simulated publish failure.
Keep a confirmation open while saving
For a provider-wide default, set dialogDefaults={{ closeOnConfirm: false }} on SubmitFlowProvider. A per-action closeOnConfirm value overrides that default. Without a provider default, the behavior can still be enabled for one action:
const flow = useSubmitFlow({
publish: {
run: publishProject,
confirm: { title: "Publish now?", description: "This will make it public." },
closeOnConfirm: false,
pendingLabel: "Publishing…",
},
});
The action runs once after confirmation. During execution, the confirm button stays visible, disabled, and displays the pending label; Cancel remains available until cancellation is requested. Success closes the dialog. Cancellation stays visible until the action settles. Failure appears inside the dialog; closing it never retries. Start a new action explicitly to try again.
Do not await another provider dialog from inside a retained dialog's action; its modal queue slot is still occupied. Show all required prompts before starting the retained action.
Notifications
Success messages close after five seconds by default. Errors remain until dismissed. Supply notify and dismiss adapters to use an existing toast system, or render SubmitFeedback inline with a flow.
See the standalone dialog example, variant and size gallery, and retained error example.