Aura Design System

Snackbar

A short message at the bottom of the screen with one optional action.

Preview

Message archived

import { Snackbar, SnackbarAction } from "@/components/ui/Snackbar";

export function SnackbarDemo() {
  return (
    <Snackbar position="inline" action={<SnackbarAction>Undo</SnackbarAction>}>
      Message archived
    </Snackbar>
  );
}

Installation

Make sure that namespace is set in your component.json file. Namespace docs: Learn more about namespaces

pnpm dlx shadcn@latest add @aura/snackbar

Manual

Install the following dependencies:

pnpm install @radix-ui/react-icons

Copy and paste the class names utility into your utils/class-names.ts file.

import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs))
}

Copy and paste the Snackbar component into your components/ui/Snackbar.tsx file.

"use client";

import * as React from "react";
import { Cross2Icon } from "@radix-ui/react-icons";
import { cn } from "@/utils/class-names";

type SnackbarProps = React.ComponentProps<"div"> & {
  open?: boolean;
  onOpenChange?: (open: boolean) => void;
  action?: React.ReactNode;
  position?: "fixed" | "inline";
};

function Snackbar({
  className,
  open = true,
  onOpenChange,
  action,
  position = "fixed",
  children,
  ...props
}: SnackbarProps) {
  if (!open) return null;
  return (
    <div
      role="status"
      data-slot="snackbar"
      className={cn(
        "z-50 flex max-w-full items-center gap-1 rounded-sm bg-gray-12 px-1.5 py-1 text-gray-1 shadow-md",
        position === "fixed"
          ? "fixed inset-x-2 bottom-2 mx-auto w-fit"
          : "relative w-fit",
        className,
      )}
      {...props}
    >
      <p className="m-0">{children}</p>
      {action}
      {onOpenChange ? (
        <button
          type="button"
          aria-label="Dismiss"
          className="inline-flex size-2 items-center justify-center rounded-sm text-gray-1 hover:bg-gray-a3 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent-8"
          onClick={() => onOpenChange(false)}
        >
          <Cross2Icon className="icon" />
        </button>
      ) : null}
    </div>
  );
}

function SnackbarAction({
  className,
  ...props
}: React.ComponentProps<"button">) {
  return (
    <button
      type="button"
      data-slot="snackbar-action"
      className={cn(
        "font-medium text-accent-9 hover:text-accent-10 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent-8",
        className,
      )}
      {...props}
    />
  );
}

export { Snackbar, SnackbarAction };