Alert
Displays a callout for user attention with contextual feedback messages.
Heads up!
Installation
Install the component Alert in your project using the CLI.
Alert.tsx
pnpm dlx behsseui@latest add AlertInstall the component manually.
Create a ui folder at the root of the project, then a component folder inside it, and finally a Alert.tsx file in that folder.
Copy and paste the following code into your project.
ui/components/Alert.tsx
1import type { HTMLAttributes, ReactNode } from "react"2import { Slot, type AsChildProps } from "./internals/Slot"3import { cva, type VariantProps } from "class-variance-authority"4import { cn } from "@/lib/utils"56const alertVariants = cva(7 "relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground",8 {9 variants: {10 variant: {11 default: "bg-background text-foreground",12 destructive: "border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive",13 success: "border-green-500/50 text-green-600 dark:text-green-500 [&>svg]:text-green-600 dark:[&>svg]:text-green-500",14 warning: "border-yellow-500/50 text-yellow-600 dark:text-yellow-500 [&>svg]:text-yellow-600 dark:[&>svg]:text-yellow-500",15 info: "border-blue-500/50 text-blue-600 dark:text-blue-500 [&>svg]:text-blue-600 dark:[&>svg]:text-blue-500",16 },17 },18 defaultVariants: {19 variant: "default",20 },21 }22)2324const alertTitleVariants = cva("mb-1 font-medium leading-none tracking-tight")2526const alertDescriptionVariants = cva("text-sm [&_p]:leading-relaxed")2728type AlertProps = AsChildProps<29 {30 children: ReactNode31 className?: string32 } & VariantProps<typeof alertVariants>,33 HTMLAttributes<HTMLDivElement>34>3536export function Alert({37 children,38 className,39 variant = "default",40 asChild,41 ...props42}: AlertProps) {43 const Component = asChild ? Slot : "div"44 return (45 <Component46 role="alert"47 className={cn(alertVariants({ variant, className }))}48 {...props}49 >50 {children}51 </Component>52 )53}5455type AlertTitleProps = AsChildProps<56 {57 children: ReactNode58 className?: string59 },60 HTMLAttributes<HTMLHeadingElement>61>6263export function AlertTitle({64 children,65 className,66 asChild,67 ...props68}: AlertTitleProps) {69 const Component = asChild ? Slot : "h5"70 return (71 <Component className={cn(alertTitleVariants(), className)} {...props}>72 {children}73 </Component>74 )75}7677type AlertDescriptionProps = AsChildProps<78 {79 children: ReactNode80 className?: string81 },82 HTMLAttributes<HTMLParagraphElement>83>8485export function AlertDescription({86 children,87 className,88 asChild,89 ...props90}: AlertDescriptionProps) {91 const Component = asChild ? Slot : "div"92 return (93 <Component className={cn(alertDescriptionVariants(), className)} {...props}>94 {children}95 </Component>96 )97}98For Alert.tsx to work, create an internals folder inside ui/component, then create a Slot.tsx file in it with the following code.
ui/components/internals/Slot.tsx
1import {2 Children,3 cloneElement,4 isValidElement,5 type HTMLAttributes,6 type PropsWithChildren,7 type ReactElement,8 type ReactNode,9} from "react";10import { twMerge } from "tailwind-merge";1112// Type utilitaire pour simplifier le typage des composant supportant asChild13export type AsChildProps<BaseProps, SecondaryProps = {}> =14 | ({ asChild: true; children: ReactNode } & BaseProps)15 | ({ asChild?: false; children?: ReactNode } & BaseProps & SecondaryProps);1617export function Slot(props: PropsWithChildren<HTMLAttributes<HTMLElement>>) {18 const children = Children.toArray(props.children).filter((c) =>19 isValidElement(c)20 );2122 if (children.length !== 1) {23 throw new Error("Slot must have exactly one child element");24 }2526 const child = children[0] as ReactElement<HTMLAttributes<HTMLElement>>;2728 return cloneElement(child, {29 ...props,30 ...child.props,31 style:32 props.style || child.props.style33 ? {34 ...props.style,35 ...child.props.style,36 }37 : undefined,38 className:39 props.className || child.props.className40 ? twMerge(props.className, child.props.className)41 : undefined,42 });43}Usages
Different variants and use cases for the Alert component.
Default
A default alert for general information.
Heads up!
Default.tsx
<Alert>
<AlertTitle>Heads up!</AlertTitle>
<AlertDescription>You can add components to your app using the CLI.</AlertDescription>
</Alert>With Icon
An alert with an icon. Place the icon component as the first child of Alert.
Heads up!
With Icon.tsx
import Info from "@/ui/icons/Info"
<Alert>
<Info />
<AlertTitle>Heads up!</AlertTitle>
<AlertDescription>You can add components to your app using the CLI.</AlertDescription>
</Alert>Destructive
An alert for error or destructive messages.
Error
Destructive.tsx
import AlertCircle from "@/ui/icons/AlertCircle"
<Alert variant="destructive">
<AlertCircle />
<AlertTitle>Error</AlertTitle>
<AlertDescription>Your session has expired. Please log in again.</AlertDescription>
</Alert>Success
An alert for success messages.
Success
Success.tsx
import CheckCircle from "@/ui/icons/CheckCircle"
<Alert variant="success">
<CheckCircle />
<AlertTitle>Success</AlertTitle>
<AlertDescription>Your changes have been saved successfully.</AlertDescription>
</Alert>Warning
An alert for warning messages.
Warning
Warning.tsx
import AlertTriangle from "@/ui/icons/AlertTriangle"
<Alert variant="warning">
<AlertTriangle />
<AlertTitle>Warning</AlertTitle>
<AlertDescription>Your account is about to expire.</AlertDescription>
</Alert>Info
An alert for informational messages.
Info
Info.tsx
import Info from "@/ui/icons/Info"
<Alert variant="info">
<Info />
<AlertTitle>Info</AlertTitle>
<AlertDescription>A new version is available.</AlertDescription>
</Alert>