my/ui

Command Palette

Search for a command to run...

All components

Toast

notifications

Origin UI component.

responsive · 480px

Install

Same command in any shadcn project — React (Vite/CRA), Next.js, Remix, Astro, and more:

$npx shadcn@latest add https://your-domain/r/toast.json

Usage

"use client";

import { useState } from "react";
import {
  Toast,
  ToastAction,
  ToastClose,
  ToastDescription,
  ToastProvider,
  ToastTitle,
  ToastViewport,
} from "@/registry/origin-ui/toast";

export default function Demo() {
  const [open, setOpen] = useState(false);
  const [destructiveOpen, setDestructiveOpen] = useState(false);

  return (
    <div className="flex flex-col items-center justify-center gap-4 min-h-[200px] p-8">
      <ToastProvider>
        <div className="flex gap-3">
          <button
            className="rounded-md bg-primary px-4 py-2 text-sm text-primary-foreground hover:bg-primary/90"
            onClick={() => setOpen(true)}
          >
            Show Toast
          </button>
          <button
            className="rounded-md bg-destructive px-4 py-2 text-sm text-white hover:bg-destructive/90"
            onClick={() => setDestructiveOpen(true)}
          >
            Show Error Toast
          </button>
        </div>

        <Toast open={open} onOpenChange={setOpen}>
          <div className="grid gap-1">
            <ToastTitle>File saved successfully</ToastTitle>
            <ToastDescription>
              Your changes have been saved to the cloud.
            </ToastDescription>
          </div>
          <ToastAction altText="Undo save">Undo</ToastAction>
          <ToastClose />
        </Toast>

        <Toast open={destructiveOpen} onOpenChange={setDestructiveOpen} variant="destructive">
          <div className="grid gap-1">
            <ToastTitle>Upload failed</ToastTitle>
            <ToastDescription>
              There was an error uploading your file. Please try again.
            </ToastDescription>
          </div>
          <ToastAction altText="Retry upload">Retry</ToastAction>
          <ToastClose />
        </Toast>

        <ToastViewport />
      </ToastProvider>
    </div>
  );
}

Component source

"use client";

import { cva, type VariantProps } from "class-variance-authority";
import { XIcon } from "lucide-react";
import { Toast as ToastPrimitives } from "radix-ui";
import type * as React from "react";

import { cn } from "@/lib/utils";

const ToastProvider = ToastPrimitives.Provider;

function ToastViewport({
  className,
  ...props
}: React.ComponentPropsWithoutRef<typeof ToastPrimitives.Viewport>) {
  return (
    <ToastPrimitives.Viewport
      className={cn(
        "fixed top-0 right-0 z-50 flex max-h-screen w-full flex-col-reverse p-4 sm:top-auto sm:bottom-0 sm:flex-col md:max-w-[400px]",
        className,
      )}
      {...props}
    />
  );
}

const toastVariants = cva(
  "group data-[state=closed]:fade-out-80 data-[state=open]:slide-in-from-top-full data-[state=closed]:data-[swipe-direction=left]:slide-out-to-left-full data-[state=closed]:data-[swipe-direction=right]:slide-out-to-right-full data-[state=open]:sm:slide-in-from-bottom-full pointer-events-auto relative flex w-full items-center justify-between overflow-hidden rounded-md border p-4 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[state=closed]:animate-out data-[state=open]:animate-in data-[swipe=end]:animate-out data-[swipe=move]:transition-none",
  {
    defaultVariants: {
      variant: "default",
    },
    variants: {
      variant: {
        default: "border bg-background text-foreground",
        destructive:
          "destructive group border-destructive bg-destructive text-white",
      },
    },
  },
);

function Toast({
  className,
  variant,
  ...props
}: React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root> &
  VariantProps<typeof toastVariants>) {
  return (
    <ToastPrimitives.Root
      className={cn(toastVariants({ variant }), className)}
      {...props}
    />
  );
}

function ToastAction({
  className,
  asChild = false,
  ...props
}: React.ComponentPropsWithoutRef<typeof ToastPrimitives.Action>) {
  return (
    <ToastPrimitives.Action
      asChild={asChild}
      className={cn(
        !asChild &&
          "inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 font-medium text-sm outline-none transition-[color,box-shadow] hover:bg-secondary focus:ring-ring focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-muted/40 focus:group-[.destructive]:ring-destructive hover:group-[.destructive]:border-destructive/30 hover:group-[.destructive]:bg-destructive hover:group-[.destructive]:text-white",
        className,
      )}
      {...props}
    >
      {props.children}
    </ToastPrimitives.Action>
  );
}

function ToastClose({
  className,
  asChild = false,
  ...props
}: React.ComponentPropsWithoutRef<typeof ToastPrimitives.Close>) {
  return (
    <ToastPrimitives.Close
      asChild={asChild}
      className={cn(
        !asChild &&
          "group absolute top-3 right-3 flex size-7 items-center justify-center rounded outline-none transition-[color,box-shadow] focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none",
        className,
      )}
      toast-close=""
      {...props}
    >
      {asChild ? (
        props.children
      ) : (
        <XIcon
          aria-hidden="true"
          className="opacity-60 transition-opacity group-hover:opacity-100"
          size={16}
        />
      )}
    </ToastPrimitives.Close>
  );
}

function ToastTitle({
  className,
  ...props
}: React.ComponentPropsWithoutRef<typeof ToastPrimitives.Title>) {
  return (
    <ToastPrimitives.Title
      className={cn("font-medium text-sm", className)}
      {...props}
    />
  );
}

function ToastDescription({
  className,
  ...props
}: React.ComponentPropsWithoutRef<typeof ToastPrimitives.Description>) {
  return (
    <ToastPrimitives.Description
      className={cn("text-muted-foreground text-sm", className)}
      {...props}
    />
  );
}

type ToastProps = React.ComponentPropsWithoutRef<typeof Toast>;

type ToastActionElement = React.ReactElement<typeof ToastAction>;

export {
  Toast,
  ToastAction,
  ToastClose,
  ToastDescription,
  ToastProvider,
  ToastTitle,
  ToastViewport,
  type ToastActionElement,
  type ToastProps,
};

Dependencies

radix-ui

Source: Origin UI