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

Snackbar

Transient bar at the bottom of the screen, with an optional action.

GH

Grace HopperCompiler notes attached
09:41

LT

Linus TorvaldsJust a hobby, won’t be big.
Mon

MH

Margaret HamiltonPriority alarms are fine.
Sun
Conversation archivedUndo

Inbox

Contacts

Settings

Installation

npx axiom add snackbar
pnpm dlx axiom add snackbar
yarn dlx axiom add snackbar
bun x axiom add snackbar

Also copies: portal.

Installs: react-native-reanimated, react-native-gesture-handler, react-native-safe-area-context, react-native-worklets.

The store, timer and swipe live in use-snackbar.ts. Colors come from snackbar.default.default.{background,foreground,action} in the component tokens.

Usage

Mount <SnackbarHost /> once at the root, then show snackbars from code.

import { SnackbarHost, snackbar } from "@/components/ui/snackbar";

snackbar.show({
	message: "Message archived",
	action: { label: "Undo", onPress: unarchive },
});

API

snackbar.show(options)

OptionTypeDefaultDescription
messagestringOne line, two at most. Describes what just happened.
action{ label: string; onPress: () => void }One text action, usually “Undo” or “Retry”. Pressing it also dismisses the snackbar.
duration'short' | 'long' | 'indefinite' | number'short'short is 4s, long 8s (default when there is an action). indefinite stays until dismissed or replaced.
iconIconNameLeading icon. Use sparingly: the message should be enough.
onDismiss(reason: 'timeout' | 'action' | 'swipe' | 'replaced' | 'dismiss') => voidCalled when the snackbar leaves, with the reason. Commit a destructive action here if it wasn’t undone.

Only one snackbar is visible. Calling show while one is on screen replaces it: the current one slides out with reason 'replaced', the new one slides in.

MethodDescription
snackbar.dismiss()Hides the current snackbar.

SnackbarHost

PropTypeDefaultDescription
bottomOffsetnumber0Extra space on top of the default gap (bottom safe area + 16pt), to sit above a BottomTabBar or a ToolBar.
swipeToDismissbooleantrueSwipe down, left or right to dismiss.

The snackbar uses background.inverse and content.inverse, so it stands out in both light and dark. It is announced to screen readers when it appears, and its duration is extended when a screen reader is on.

Use cases

Undo a destructive action

Delete right away, offer Undo, and only commit on the server when the snackbar leaves without it.

Book flights

Renew passport
Pack chargers
”Buy adapters” deletedUndo
function deleteTask(task: Task) {
	hideLocally(task.id);
	snackbar.show({
		message: `"${task.title}" deleted`,
		action: { label: "Undo", onPress: () => restoreLocally(task.id) },
		onDismiss: (reason) => reason !== "action" && api.deleteTask(task.id),
	});
}

Retry after a network error

The action gives a way to try again without hunting for the button that failed.

See you at 7!

I’ll bring the photos

Message not sentRetry

Message

sendMutation.mutate(message, {
	onError: () =>
		snackbar.show({
			message: "Message not sent",
			duration: "indefinite",
			action: {
				label: "Retry",
				onPress: () => sendMutation.mutate(message),
			},
		}),
});

Above a tab bar

bottomOffset keeps the snackbar above the navigation, so it never covers the tabs.

Added to FavoritesView

Home

Search

Favorites

<SnackbarHost bottomOffset={tabBarHeight} />