Build websites 10x faster with HextaUI Blocks — Learn more
UI/UI

Alert

A versatile alert component for displaying important messages, notifications, and status updates.

<div className="space-y-4">
  <Alert>This is a basic alert message.</Alert>
  <Alert variant="destructive">
    Something went wrong. Please try again.
  </Alert>
  <Alert variant="warning">This action cannot be undone.</Alert>
  <Alert variant="success">Your changes have been saved.</Alert>
  <Alert variant="info">New features are now available.</Alert>
</div>

Installation

Install following dependencies:

npm install class-variance-authority lucide-react motion
pnpm add class-variance-authority lucide-react motion
yarn add class-variance-authority lucide-react motion
bun add class-variance-authority lucide-react motion

Copy and paste the following code into your project.

components/ui/alert.tsx
"use client";

import * as React from "react";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils";
import { type LucideIcon, X } from "lucide-react";
import { motion, AnimatePresence } from "motion/react";

const alertVariants = cva(
  "relative w-full rounded-lg border p-4 text-sm transition-colors",
  {
    variants: {
      variant: {
        default:
          "border-[hsl(var(--hu-border))] bg-[hsl(var(--hu-card))] text-[hsl(var(--hu-card-foreground))]",
        destructive:
          "border-[hsl(var(--hu-destructive))] bg-[hsl(var(--hu-destructive))]/10 text-[hsl(var(--hu-destructive))] [&>svg]:text-[hsl(var(--hu-destructive))]",
        warning:
          "border-amber-200 bg-amber-50 text-amber-800 dark:border-amber-700 dark:bg-amber-950/30 dark:text-amber-200 [&>svg]:text-amber-600 dark:[&>svg]:text-amber-400",
        success:
          "border-green-200 bg-green-50 text-green-800 dark:border-green-700 dark:bg-green-950/30 dark:text-green-200 [&>svg]:text-green-600 dark:[&>svg]:text-green-400",
        info: "border-blue-200 bg-blue-50 text-blue-800 dark:border-blue-700 dark:bg-blue-950/30 dark:text-blue-200 [&>svg]:text-blue-600 dark:[&>svg]:text-blue-400",
      },
    },
    defaultVariants: {
      variant: "default",
    },
  },
);

export interface AlertProps
  extends React.HTMLAttributes<HTMLDivElement>,
    VariantProps<typeof alertVariants> {
  icon?: LucideIcon;
  title?: string;
  dismissible?: boolean;
  onDismiss?: () => void;
}

const Alert = React.forwardRef<HTMLDivElement, AlertProps>(
  (
    {
      className,
      variant,
      icon: Icon,
      title,
      dismissible,
      onDismiss,
      children,
      ...props
    },
    ref,
  ) => {
    const [isVisible, setIsVisible] = React.useState(true);

    const handleDismiss = () => {
      setIsVisible(false);
      setTimeout(() => {
        onDismiss?.();
      }, 150); // Match the exit animation duration
    };

    // Extract motion-conflicting props
    const {
      onDrag,
      onDragStart,
      onDragEnd,
      onAnimationStart,
      onAnimationEnd,
      onAnimationIteration,
      onTransitionEnd,
      ...motionProps
    } = props;

    return (
      <AnimatePresence>
        {isVisible && (
          <motion.div
            ref={ref}
            className={cn(alertVariants({ variant }), className)}
            initial={{ opacity: 0, y: -10, scale: 0.95 }}
            animate={{ opacity: 1, y: 0, scale: 1 }}
            exit={{ opacity: 0, y: -10, scale: 0.95 }}
            transition={{ duration: 0.15, ease: "easeOut" }}
            role="alert"
            {...motionProps}
          >
            <div className="flex">
              {Icon && (
                <div className="flex-shrink-0">
                  <Icon className="h-4 w-4 mt-0.5" />
                </div>
              )}
              <div className={cn("flex-1", Icon && "ml-3")}>
                {title && <h3 className="text-sm font-medium mb-1">{title}</h3>}
                <div
                  className={cn("text-sm", title && "text-muted-foreground")}
                >
                  {children}
                </div>
              </div>
              {dismissible && (
                <div className="flex-shrink-0 ml-3">
                  <button
                    type="button"
                    className="inline-flex rounded-md p-1.5 transition-colors hover:bg-black/5 dark:hover:bg-white/5 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-[hsl(var(--hu-ring))]"
                    onClick={handleDismiss}
                    aria-label="Dismiss alert"
                  >
                    <X className="h-4 w-4" />
                  </button>
                </div>
              )}
            </div>
          </motion.div>
        )}
      </AnimatePresence>
    );
  },
);

Alert.displayName = "Alert";

export { Alert, alertVariants };
components/ui/AlertWithIcon.tsx
"use client";

import { Alert, type AlertProps } from "../alert";
import { type LucideIcon } from "lucide-react";
import {
  AlertTriangle,
  CheckCircle,
  Info,
  X,
  AlertCircle,
  Shield,
  Bell,
  Star,
  Zap,
  Heart,
  Settings,
  User,
  Mail,
  Server,
  CreditCard,
  Trophy,
  HardDrive,
} from "lucide-react";

// Map of icon names to actual components
const iconMap = {
  AlertTriangle,
  CheckCircle,
  Info,
  X,
  AlertCircle,
  Shield,
  Bell,
  Star,
  Zap,
  Heart,
  Settings,
  User,
  Mail,
  Server,
  CreditCard,
  Trophy,
  HardDrive,
} as const;

export type IconName = keyof typeof iconMap;

interface AlertWithIconProps extends Omit<AlertProps, "icon"> {
  iconName?: IconName;
  icon?: LucideIcon;
}

export function AlertWithIcon({
  iconName,
  icon,
  ...props
}: AlertWithIconProps) {
  // Priority: custom icon > iconName > no icon
  let IconComponent: LucideIcon | undefined;

  if (icon) {
    IconComponent = icon;
  } else if (iconName) {
    IconComponent = iconMap[iconName];
  }

  return <Alert icon={IconComponent} {...props} />;
}
npx hextaui@latest add alert
pnpm dlx hextaui@latest add alert
yarn dlx hextaui@latest add alert
bun x hextaui@latest add alert

Usage

import { Alert, AlertWithIcon } from "@/components/ui/alert";
import { Database, Rocket } from "lucide-react";

Basic Alert

<Alert variant="default">This is a default alert.</Alert>
<Alert variant="destructive">This is a destructive alert.</Alert>
<Alert variant="warning">This is a warning alert.</Alert>
<Alert variant="success">This is a success alert.</Alert>
<Alert variant="info">This is an info alert.</Alert>

Alert with Predefined Icons

<AlertWithIcon iconName="Info" variant="info" title="Information">
  This uses a predefined icon from the available icon set.
</AlertWithIcon>

Alert with Custom Icons

<AlertWithIcon icon={Database} variant="warning" title="Database Alert">
  This uses a custom Lucide icon component for maximum flexibility.
</AlertWithIcon>

Examples

Variants

<div className="space-y-4">
  <Alert>
    <strong>Default Alert:</strong> This is a basic alert message.
  </Alert>
  <Alert variant="destructive">
    <strong>Error:</strong> Something went wrong. Please try again.
  </Alert>
  <Alert variant="warning">
    <strong>Warning:</strong> This action cannot be undone.
  </Alert>
  <Alert variant="success">
    <strong>Success:</strong> Your changes have been saved.
  </Alert>
  <Alert variant="info">
    <strong>Info:</strong> New features are now available.
  </Alert>
</div>

With Titles

<div className="space-y-4">
  <Alert title="System Update">
    A new version of the application is available. Please restart to apply
    updates.
  </Alert>
  <Alert variant="destructive" title="Connection Failed">
    Unable to connect to the server. Please check your internet connection.
  </Alert>
  <Alert variant="warning" title="Data Loss Warning">
    You have unsaved changes. Leaving this page will discard your work.
  </Alert>
  <Alert variant="success" title="Upload Complete">
    Your files have been successfully uploaded to the cloud storage.
  </Alert>
  <Alert variant="info" title="Feature Preview">
    Try out our new dashboard features in beta mode.
  </Alert>
</div>

With Icons

import { AlertWithIcon } from "@/components/ui/Alert";
import { Wifi, Database, Rocket, Coffee } from "lucide-react";

<div className="space-y-4">
  {/* Using predefined icon names */}
  <AlertWithIcon
    iconName="Info"
    variant="info"
    title="New Features Available"
  >
    We've added new collaboration tools to help you work better with your team.
  </AlertWithIcon>
  <AlertWithIcon
    iconName="CheckCircle"
    variant="success"
    title="Payment Successful"
  >
    Your subscription has been renewed for another year.
  </AlertWithIcon>

  {/* Using custom icon components */}
  <AlertWithIcon
    icon={Wifi}
    variant="info"
    title="Network Connected"
  >
    You are now connected to the secure network.
  </AlertWithIcon>
  <AlertWithIcon
    icon={Database}
    variant="warning"
    title="Database Backup Required"
  >
    It's been 7 days since your last backup. Consider backing up your data.
  </AlertWithIcon>
  <AlertWithIcon
    icon={Rocket}
    variant="success"
    title="Deployment Successful"
  >
    Your application has been deployed to production successfully.
  </AlertWithIcon>
</div>

With Custom Icons

import { AlertWithIcon } from "@/components/ui/Alert";
import { Code, Sparkles, TrendingUp, Lock, Calendar } from "lucide-react";

<div className="space-y-4">
  <AlertWithIcon
    icon={Code}
    variant="info"
    title="Code Review Ready"
  >
    Your pull request is ready for code review by the team.
  </AlertWithIcon>
  <AlertWithIcon
    icon={Sparkles}
    variant="success"
    title="Feature Unlocked"
  >
    Congratulations! You've unlocked premium features.
  </AlertWithIcon>
  <AlertWithIcon
    icon={TrendingUp}
    variant="info"
    title="Performance Improved"
  >
    Your application performance has increased by 40% this month.
  </AlertWithIcon>
  <AlertWithIcon
    icon={Lock}
    variant="warning"
    title="Security Alert"
  >
    We detected unusual login activity. Please verify your account.
  </AlertWithIcon>
  <AlertWithIcon
    icon={Calendar}
    variant="default"
    title="Meeting Reminder"
  >
    Your team standup meeting starts in 15 minutes.
  </AlertWithIcon>
</div>

Dismissible Alerts

import { Bell, Trophy, HardDrive } from "lucide-react";

<div className="space-y-4">
  <Alert
    icon={Bell}
    variant="info"
    title="Notification"
    dismissible
    onDismiss={() => console.log("Alert dismissed")}
  >
    You have 3 new messages in your inbox.
  </Alert>
  <Alert
    icon={Trophy}
    variant="success"
    title="Achievement Unlocked"
    dismissible
    onDismiss={() => console.log("Achievement dismissed")}
  >
    Congratulations! You've completed 100 tasks this month.
  </Alert>
  <Alert
    icon={HardDrive}
    variant="warning"
    title="Storage Warning"
    dismissible
    onDismiss={() => console.log("Storage warning dismissed")}
  >
    Your storage is almost full. Consider upgrading your plan.
  </Alert>
</div>

Custom Styling

<div className="space-y-4">
  <Alert className="rounded-xl border-2">
    <strong>Rounded Alert:</strong> Custom border radius styling.
  </Alert>
  <Alert variant="info" className="border-dashed">
    <strong>Dashed Border:</strong> Alert with dashed border style.
  </Alert>
  <Alert variant="success" className="shadow-lg">
    <strong>With Shadow:</strong> Alert with enhanced shadow.
  </Alert>
  <Alert className="bg-gradient-to-r from-purple-50 to-pink-50 border-purple-200 text-purple-800 dark:from-purple-950/20 dark:to-pink-950/20 dark:border-purple-800 dark:text-purple-200">
    <strong>Gradient Background:</strong> Custom gradient styling.
  </Alert>
</div>

Real-world Examples

System Status

Account Security

Billing Information

import { Server, Shield, CreditCard } from "lucide-react";

<div className="space-y-4">
  <div className="space-y-2">
    <h4 className="text-sm font-medium">System Status</h4>
    <Alert icon={Server} variant="success" title="All Systems Operational">
      All services are running normally. Last updated 2 minutes ago.
    </Alert>
  </div>

  <div className="space-y-2">
    <h4 className="text-sm font-medium">Account Security</h4>
    <Alert icon={Shield} variant="warning" title="Password Expiring Soon">
      Your password will expire in 3 days. Update it now to maintain account security.
    </Alert>
  </div>

  <div className="space-y-2">
    <h4 className="text-sm font-medium">Billing Information</h4>
    <Alert icon={CreditCard} variant="destructive" title="Payment Method Required">
      Your trial ends in 2 days. Add a payment method to continue using our services.
    </Alert>
  </div>
</div>

Props

Alert Props

PropTypeDefault
children?
ReactNode
undefined
className?
string
undefined
onDismiss?
() => void
undefined
dismissible?
boolean
false
title?
string
undefined
icon?
LucideIcon
undefined
variant?
"default" | "destructive" | "warning" | "success" | "info"
"default"

AlertWithIcon Props

The AlertWithIcon component extends all the base Alert props and adds icon-specific functionality:

PropTypeDefault
icon?
LucideIcon
undefined
iconName?
"AlertTriangle" | "CheckCircle" | "Info" | "X" | "AlertCircle" | "Shield" | "Bell" | "Star" | "Zap" | "Heart" | "Settings" | "User" | "Mail" | "Server" | "CreditCard" | "Trophy" | "HardDrive"
undefined

Icon Priority: When both icon and iconName are provided, the custom icon prop takes precedence over iconName.

Edit on GitHub

Last updated on