Do Not Close

Warns before closing the tab mid-payment, upload or migration and says clearly when it is safe to close, so work isn't cut off halfway.

StopsClosed the tab mid-payment

Installation

pnpm dlx shadcn@latest add https://bollard-ui.vercel.app/r/do-not-close.json

The files land in your project. They're yours to edit.

Usage

do-not-close-usage.tsx
import { DoNotClose, type OperationStatus } from "@/components/ui/do-not-close"

<DoNotClose
  status={status}
  progress={progress}
  label="Uploading 312 invoices (48 MB)"
  doneLabel="All 312 invoices uploaded"
  error="The connection dropped. Retry uploads only the invoices that are missing."
  onRetry={start}
/>

What it catches

  • Closing or reloading the tab while work is running (beforeunload warning)
  • Unclear progress, with a rolling-rate time-left estimate and a stalled notice
  • Uncertainty at the end, with an explicit "Safe to close"
  • Failures, with a Retry

Props

DoNotClose props
PropTypeDefault
status* (required)

`active` while the operation runs. `idle` renders nothing.

OperationStatus—
progress

0 to 1. Leave undefined when progress is unknown.

number—
label* (required)

What is happening, e.g. "Processing your payment".

string—
doneLabel

Shown when finished, e.g. "Payment confirmed".

string"All done"
error

What went wrong, shown in the `error` state.

string—
onRetry

Shows a Retry action in the `error` state.

() => void—
activeTitle

Headlines per state; `{label}` is filled in.

string"Don't close or refresh this tab"
doneTitle

Heading shown when it is safe to close.

string"Safe to close"
errorTitle

Heading shown when the work failed.

string"{label} didn't finish"
retryLabel

Retry button text.

string"Retry"
stalledText

Shown when progress has stalled.

string"taking longer than usual"
estimatingText

Shown while the time left is being estimated.

string"estimating time left"
titlePrefix

Prefix added to the tab title while active.

string"⏳ Don't close · "
doneTitlePrefix

Prefix shown in the tab title when it finished while the user was in another tab.

string"✓ Safe to close · "
etaWindow

Rolling window for the time-left estimate, in ms.

number8000

* required. Also accepts the native props of <div> (className, aria-*, data-*…), passed through to it.

Accessibility

  • role="progressbar" with aria-valuenow and an aria-valuetext including time left
  • Status in a role="status" region; failures use role="alert"
  • Retry is a labelled button

Only what the source does. Test with your own screen reader and content before shipping.

Limitations

This is a UX guard rail. Enforce on the server: Make long operations resumable or idempotent on the server, so a closed tab or retry doesn't leave work half done or duplicated.

  • Browsers decide whether to show the close warning and use their own text; mobile browsers often skip it
  • Can't prevent a crash, power loss or the OS killing the tab

What it stops

Closed the tab mid-payment

Also known as

do not close · beforeunload · payment processing · upload progress · migration · do not refresh · do not press back · time left

Field type

Dependencies

Pairs well with