Impact Preview

Before a delete, loads and lists everything that goes with the parent object, so a cascade doesn't wipe child records nobody saw.

StopsHelps prevent a cascade delete wiping child records

Installation

pnpm dlx shadcn@latest add https://bollard-ui.vercel.app/r/impact-preview.json

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

Usage

impact-preview-usage.tsx
import { ImpactPreview, type ImpactGroup } from "@/components/ui/impact-preview"

<ImpactPreview
  key={run}
  name="Acme"
  load={async (signal) => {
    await wait(900, signal)
    if (failNext) throw new Error("Network error")
    return ACME
  }}
  onConfirm={() =>
    new Promise<void>((resolve) =>
      setTimeout(() => {
        setDeleted(true)
        resolve()
      }, 700),
    )
  }
  onCancel={() => setRun((n) => n + 1)}
/>

What it catches

  • Child records a cascade will delete
  • Records that will be detached rather than deleted
  • Confirming before the impact has loaded: Confirm stays locked
  • Load failures, with a retry

Props

ImpactPreview props
PropTypeDefault
name* (required)

Name of the thing being deleted, e.g. "Acme".

string—
load* (required)

Fetches the dependents. Receives an AbortSignal that fires on unmount or retry.

(signal: AbortSignal) => Promise<ImpactGroup[]>—
onConfirm* (required)

Runs when the user confirms. Return a promise to show a pending state; a rejection is caught.

() => void | Promise<void>—
onCancel

Called when the user cancels.

() => void—
title

Heading, given the parent's name.

(name: string) => React.ReactNode(n) => `Delete “${n}”?`
confirmLabel

Confirm label, given the parent's name and how many dependents get deleted with it.

(name: string, deletedCount: number) => React.ReactNode(n, count) => count > 0 ? `Delete ${n} and ${count} ${cou…
pendingLabel

Confirm button content while onConfirm is pending.

React.ReactNode"Deleting…"
cancelLabel

Cancel button content.

React.ReactNode"Cancel"
retryLabel

Retry button content after a load error.

React.ReactNode"Retry"
loadingMessage

Shown while dependents load.

(name: string) => React.ReactNode(n) => `Checking what depends on ${n}…`
errorMessage

Shown when the lookup fails. Deleting stays blocked until a retry succeeds.

(name: string) => React.ReactNode(n) => `Couldn’t check what depends on ${n}. Deleting is …

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

Accessibility

  • Dialog content labelled by its title and aria-busy while loading
  • Loading text in a polite live region; errors use role=alert
  • Groups expand with aria-expanded and aria-controls
  • Locked Confirm uses aria-disabled so it stays focusable

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: Run the delete in a transaction with the same cascade rules and authorization on the server.

  • Accurate only if your load function mirrors the real cascade rules
  • The list can be stale if data changes between preview and delete
  • Not authorization; it doesn't decide who may delete

What it stops

Helps prevent a cascade delete wiping child records

Also known as

cascade delete · delete preview · related records · dependencies · foreign key · delete folder · delete workspace

Dependencies

Pairs well with