Skip to content

Undo Commit Toast

An undo window that defers the action instead of reversing it, and pauses when you tab toward the button.

Prominimaleditorial
Category
Patterns
Added
2026-09-21
Deps
none
Updated
2026-09-21

This is a viberdy Pro component

The live preview, the props panel and the write-up are open to everyone. The full source, the AI prompt and CLI install are part of Pro.

Preview
  • Q4 pricing deck.key
  • Warehouse audit — final.pdf
  • Brand marks (archive).zip
  • Supplier list 2026.csv
  • remove a row, then tab to Undo

Props

6000
Pause on hover
No runtime dependencies
"use client";

import { useEffect, useRef, useState } from "react";

type Pending<T> = {
  id: number;
  item: T;
  index: number;
  /** Absolute time the commit is due. Null while paused. */
  deadline: number | null;
  remaining: number;
};

export function UndoCommitToast<T extends { id: string; label: string }>({
  items: initial,
  undoWindow = 6000,
  pauseOnHover = true,
  onCommit,

  // ---------------------------------------------------------------
  // Full source available with a viberdy Pro subscription.
  // https://viberdy.dev/pricing
  // ---------------------------------------------------------------
}
Notes

About this pattern

Undo is usually built backwards: the destructive thing happens straight away and the undo button tries to put it back. That works for a local array and falls apart for anything real, because reversal cannot restore a server-generated id, a created timestamp or a position in an ordered list, cannot re-establish relations that were cascaded away, and cannot unsend whatever the action triggered. The correct shape is to DEFER — hide the row immediately so the interface still feels instant, and hold the actual work until the window expires. Nothing needs undoing because nothing has happened. Three details make the window itself behave. The countdown is an absolute deadline paired with a remaining duration rather than a single timeout, because a timeout cannot be paused and resumed at the exact point it stopped. The ring is animated by writing its dash offset straight to the element, since a state update per frame re-renders the whole list sitting behind the toast. And the timer pauses on FOCUS as well as hover, which is the detail almost everyone misses and is a plain accessibility failure when it is missing: a keyboard user tabbing toward the button cannot reach it inside a six-second window. Pending commits are also flushed on unmount, or someone removes a row, navigates away, and watches it silently return.

Curator’s note

Pause the countdown on focus, not only on hover. A keyboard user cannot reach the Undo button inside six seconds otherwise.

Related

Pairs well with

Matched on shared tags and category — the entries most likely to be used alongside this one.

Featured
NewPro

The error pattern that actually works with a screen reader: focus the summary, link to the fields, punish late.

formvalidationa11y
Featured
NewPro

A button that survives a real round trip — a minimum pending duration, a width that never twitches, aria-disabled instead of disabled.

buttonasyncloading
Featured
NewPro

A sheet on the native dialog element — no hand-rolled focus trap, and the three things the platform leaves you fixed.

dialogmodalsheet