Undoable action
Pre-alphaThe registry and the CLI are not published yet.Roadmap

Undoable action

An action that happens right away and can be cancelled for a few seconds.

Draft

Specified, not implemented yet. It relies on Snackbar and its onDismiss reason. The API may change.

Instead of asking “Are you sure?”, the app does what the user asked and gives them a few seconds to take it back. Common actions stay fast, and a mistake costs one tap.

Notifications
Order shipped9:41Your headphones are on the way.
Dentist tomorrow9:0210:30 at Harley Street.
Ada joined the team8:15Say hello in #general.
Payment receivedYesterday£42.00 from Alan.
New reviewYesterday"Fast delivery, great fit."
Price dropMonThe desk lamp is 20% off.
All caught up
Archived
pending none
server nothing sent

Tap the archive button on a notification. The row leaves right away, but the footer shows that nothing reached the server yet. Tap Undo and the row comes back in its place. Let the snackbar run out, archive a second row, or press Leave screen, and the request leaves.

The same action, frame by frame:

Notifications
idle

Four rows. The user taps archive on the second.

Notifications
ArchivedUndo
pending

The row is gone, Undo is offered. Nothing sent yet.

Notifications
undo · nothing sent

Undo: the row is back in its place.

Notifications
timeout · archive() sent

No undo within 4s: the request leaves.

When to use it

  • Frequent actions on items the user owns: messages, notifications, cart items, saved searches.
  • Actions whose server call can wait a few seconds.

It works whatever triggers the action: a button in the row, a full swipe, a menu entry, a toolbar action.

Don’t use it when the action can’t be reverted, or affects more than the user sees, like a folder with its content or an account. Ask first with a Dialog.

How it works

The screen updates immediately. The request waits for the snackbar to close.

actionUndo · item back4s timeoutleave · backgroundanother action · commits this onerequest sentidlependingcommitted

  • Optimistic update. The list changes right away, so the user can keep going.
  • One pending action. A new action replaces the snackbar, which commits the previous one.
  • Commit on leave. Leaving the screen or backgrounding the app commits what’s pending.
  • Undo restores in place. The item comes back at its position, not at the end of the list.
  • If the request fails, put the item back and show the error in a Toast.

Implementation

In a screen

Conceptual
const [removed, setRemoved] = useState<string[]>([]);
const visible = messages.filter((m) => !removed.includes(m.id));

const remove = (message: Message) => {
	setRemoved((ids) => [...ids, message.id]);
	snackbar.show({
		message: "Conversation deleted",
		duration: "short",
		action: {
			label: "Undo",
			onPress: () =>
				setRemoved((ids) => ids.filter((id) => id !== message.id)),
		},
		// 'timeout', 'swipe' and 'replaced' (the next action) all commit.
		onDismiss: (reason) =>
			reason !== "action" && api.deleteMessage(message.id),
	});
};

Accessibility

  • The snackbar is announced and keeps its action focusable for its whole duration.
  • Extend the snackbar’s duration when a screen reader is on.
  • With Reduce Motion on, the item disappears and comes back without animation.