A non-disruptive message that appears at the bottom of the interface to provide quick, at-a-glance feedback on the outcome of an action. Snackbars communicate messages that are minimally interruptive.

Importing

Typically, you'll invoke snackbars via the included snackbars context.

A useSnackbars hook is available as:

import { useSnackbars } from '@customerio/pluma-components/react';

In case you need to render a snackbar directly, the component can be imported:

import { Snackbar } from '@customerio/pluma-components/react';

Usage

The useSnackbars react hook returns the snackbars context, which includes a create function.

create accepts an object with the following keys:

  • message: the text to display in the snackbar. This is required.
  • id: a unique string id of the snackbar (used to prevent showing the same snackbar multiple times). If none is provided, a random id will be generated.
  • action: an action definition (see below).
  • layout: the layout of the snackbar.
  • variant: the visual variant of the snackbar (default, error, or success).
  • isError: whether the snackbar should be rendered in the error state (deprecated, use variant="error" instead).
  • shouldCloseOnAction: whether the snackbar should close when the action button is clicked.
  • autoCloseTimeout: how long to wait until the snackbar closes automatically (in milliseconds). If the snackbar has an action, the timeout will be doubled. Defaults to 5000.
  • animationTransitionDuration: the duration of the CSS enter/exit animation. Defaults to 150 (in ms).
  • offset: an offset definition (see below).

Calling create will render a snackbar at the bottom of the screen. By default, a snackbar will automatically close after 5 seconds (or 10 seconds if it has an action).

import { Button, ButtonGroup, useSnackbars } from '@customerio/pluma-components/react';

export default function Example() {
	const snackbars = useSnackbars();
	
	const handleDefaultClick = () => {
		snackbars.create({
			message: 'This is a default snackbar message',
		});
	};
	
	const handleSuccessClick = () => {
		snackbars.create({
			message: 'Operation completed successfully!',
			variant: 'success',
		});
	};
	
	const handleErrorClick = () => {
		snackbars.create({
			message: 'An error occurred',
			variant: 'error',
		});
	};
	
	return (
		<ButtonGroup groupVariant="spaced">
			<Button onClick={handleDefaultClick}>Default Snackbar</Button>
			<Button onClick={handleSuccessClick}>Success Snackbar</Button>
			<Button onClick={handleErrorClick}>Error Snackbar</Button>
		</ButtonGroup>
	);
}

SnackbarApi

Internally, useSnackbars creates an instance of a SnackbarApi class, which manages the current state and exposes the create/destroy methods. It is possible to pass in your own instance of this class, by either configuring it in the PlumaProvider's global config (below).

This is useful in situations where we want to pass around that API instance outside of the render context of a component. useSnackbars relies on context providers, and because of this can't be used in functional utilities or Ember services.

Pluma exports the class, and it can be imported and used as demonstrated below:

import { PlumaSnackbarApi } from '@customerio/pluma-components/react';

export const snackbarApi = new PlumaSnackbarApi();

return App = ({children}) => <PlumaProvider
	componentConfig={{
		PlumaSnackbar: {
			snackbarApi: snackbarApi,
		}
	}}
>
	{children}
</PlumaProvider>;

PlumaProvider configuration

By default, a snackbar invoked like demonstrated above will disappear after 5 seconds (or 10 seconds if it has an action). Additionally, it will animate in and out with a CSS opacity and translate transition, with a duration of 150ms.

These value can be customized on individual snackbars in the create context function. Additionally, they can globally configured on the PlumaProvider component. PlumaProvider accepts a componentConfig prop, which accepts a PlumaSnackbar key to override the defaults:

<PlumaProvider
	componentConfig={{
		PlumaSnackbar: {
			autoCloseTimeout: 2000,
			animationTransitionDuration: 500,
		}
	}}
>
	{children}
</PlumaProvider>

For example:

Anatomy of the Snackbar

By default, a snackbar is a short piece of text with a close button. You can make it more dynamic, however, by adding an action the user can take and allowing for more whitespace within the snackbar.

Note: The close button is only rendered when an onClose callback is provided.

Layout

The layout of the snackbar determines the positioning of the three elements: text, close button, and action button.

  • Condensed: The smallest version of a snackbar. This is the default layout, and makes all three elements inline with each other. Here the action is optional, and the snackbar can simply be a piece of text and the close button
<Snackbar
	action={{ label: 'Perform action', onClick: () => {} }}
	onClose={() => {}}
>
	This is body text for the snackbar.
	It should be short and descriptive.
</Snackbar>
  • Expanded: This is for scenarios where the combination of text and an action are too tight for the normal snackbar layout. This pushes the action button to its own row and allows the content to take up more room above. For this layout, you should always provide an action with the snackbar
<Snackbar 
	layout="expanded"
	action={{ label: 'Perform action', onClick: () => {} }}
	onClose={() => {}}
>
	This is body text for the snackbar.
	It should be short and descriptive.
</Snackbar>

Variants

Snackbars have a limited number of available variants to cut down on information overload and to keep the display of information simple.

  • Default: The default variant uses a dark background and is what you should use most often. These are not flashy and will reduce the amount of interruption caused. It's the default if another variant isn't specified.
<Snackbar
	action={{ label: 'Perform action', onClick: () => {} }}
	onClose={() => {}}
>
	This is body text for the snackbar.
	It should be short and descriptive.
</Snackbar>
  • Error: Error variants are disruptive to both screen readers and users. They appear with a critical background color and will immediately draw attention. For this reason, you should only use it for show-stopping issues like unknown or unexpected server errors. To use this variant, set the variant prop to "error".

  • Success: Success variants indicate that an action was completed successfully. They appear with a success background color that provides positive feedback. To use this variant, set the variant prop to "success".

<Snackbar
	variant="error"
	action={{ label: 'Perform action', onClick: () => {} }}
	onClose={() => {}}
>
	This is body text for the snackbar.
	It should be short and descriptive.
</Snackbar>
<Snackbar
	variant="success"
	action={{ label: 'Perform action', onClick: () => {} }}
	onClose={() => {}}
>
	This is body text for the snackbar.
	It should be short and descriptive.
</Snackbar>

General guidelines

  • Snackbars shouldn't interrupt the user experience, and they don't require user input to disappear
  • All snackbars are dismissible, except two-line with longer action
  • Only one snackbar may appear at a time. When multiple snackbar updates are necessary, they should appear one at a time
  • Snackbars without action persist for 5 seconds, with action 10 seconds
  • Snackbars won't disappear while hovering on them

Using IDs

When creating a snackbar, you have the option to provide an id. Here are recommendations for when to use (or not use) IDs:

When NOT to use IDs (most cases)

In most cases, you should not provide an ID. Even when multiple identical snackbars might appear (for example, from multiple save operations in succession), this is generally acceptable behavior. Common app patterns like disabling save buttons after the first submission naturally prevent duplicate snackbars.

// Recommended - let the system generate an ID
snackbars.create({
  message: 'Changes saved successfully',
  variant: 'success'
});

When to use IDs

You should only provide an ID when you specifically need to:

  1. Prevent truly duplicate messages: In cases where you absolutely need to prevent the same message from appearing multiple times (though this is rarely necessary in most applications).

  2. Programmatically dismiss a snackbar later: If you need to dismiss a snackbar from code rather than waiting for the timeout or user action.

Programmatically dismissing snackbars

There are two ways to programmatically dismiss a snackbar:

  1. Using the return function (recommended): The create method returns a function that can be called to dismiss that specific snackbar:
// Recommended approach for programmatic dismissal
const dismiss = snackbars.create({
  message: 'Operation in progress'
});

// Later in your code:
dismiss(); // Removes the snackbar
  1. Using the destroy method with an ID: If you provided an ID when creating the snackbar, you can later use the destroy method to dismiss it:
// Create a snackbar with a specific ID
snackbars.create({
  id: 'progress-notification',
  message: 'Operation in progress'
});

// Later in your code:
snackbars.destroy('progress-notification'); // Removes the snackbar
  1. Clearing all snackbars: If you need to dismiss all snackbars at once, you can use the destroyAll method:
// Clear all snackbars
snackbars.destroyAll();

Actions

A Snackbar's action may be a button (performing a callback) or a link. The action prop accepts an object with the following keys:

  • label: the text to display in the action button
  • onClick: a callback function to run when the action is clicked (also available on link actions)
  • isDisabled: whether the button should be disabled
  • href: a string URL. When provided, the action will render as a link (via the link component configured on the provider)

Offset

The offset prop accepts an object that defines the snackbar’s positioning relative to the viewport. It includes the following keys:

  • top (string): Specifies the distance from the top of the viewport to the snackbar.
  • bottom (string): Specifies the distance from the bottom of the viewport to the snackbar.
  • left (string): Specifies the distance from the left edge of the viewport to the snackbar.
  • right (string): Specifies the distance from the right edge of the viewport to the snackbar.

If not provided, the default offset is:

{
	bottom: themeVars.space['300'],
	left: '0',
	right: '0',
}

This configuration allows for flexible positioning of the snackbar, enabling you to place it anywhere on the screen, whether aligned to the top, bottom, or corners.

Disabling auto-close

By default, snackbars will automatically close after a set amount of time. If you want to disable this behavior, you can set the autoCloseTimeout prop to 0.

API

Whether the close button should be disabled.

Whether the snackbar should render in the error state.

Default:condensed

The layout of the snackbar to render. Affects button alignment and close functionality.

Function to call when the "X" close button is clicked. Providing this prop will render the close button.

Default:false

Whether onClose should be called as well when clicking the action button.

Default:default

The visual variant of the snackbar.

  • default: Default style with dark background
  • error: Error style with critical surface background
  • success: Success style with success surface background