Alert component is used to display important messages to the user.
This is an alert component. It's used to display important messages to the user.
export const Example = () => {
return (
<Alert side={<CircleAlert />} dismissible>
<AlertTitle>Alert!</AlertTitle>
<AlertDescription>
This is an alert component. It's used to display important messages to
the user.
</AlertDescription>
</Alert>
);
};
If you haven't included Piksel UI in your project, include it first. You can check the installation page.
Copy the following source code and add it to your project.
"use client";
import { cn } from "@/utils/cn";
import { isRenderable } from "@/utils/is-renderable";
import { Slot, Slottable } from "@radix-ui/react-slot";
import { X } from "lucide-react";
import React, {
createContext,
forwardRef,
HTMLAttributes,
useContext,
useState,
} from "react";
import { tv, VariantProps } from "tailwind-variants";
type AlertContextType = VariantProps<typeof alertStyles>;
const AlertContext = createContext<AlertContextType | null>(null);
const AlertContextProvider = AlertContext.Provider;
const useAlertContext = () => {
const context = useContext(AlertContext) || {};
return context;
};
export const alertDisplayName = "Alert";
export type AlertProps = HTMLAttributes<HTMLDivElement> &
VariantProps<typeof alertStyles> & {
/**
* If true, the alert will be rendered as a child component.
*/
asChild?: boolean;
/**
* Determines whether the alert is open or not.
*/
isOpen?: boolean;
/**
* Callback to close the alert. If provided, the alert will be dismissable.
*/
close?: () => void;
/**
* The content to be displayed on the left side of the alert.
*/
side?: React.ReactNode;
};
/**
* @name Alert
* @description Alert component is used to display important messages to the user.
*/
export const Alert = forwardRef<HTMLDivElement, AlertProps>((props, ref) => {
const {
asChild,
isOpen,
close,
side,
variant,
color,
size,
radius,
dissmisable,
className,
children,
...rest
} = props;
const [open, setOpen] = useState(isOpen ?? true);
const _open = isOpen ?? open;
const _close = close ?? (() => setOpen(false));
const _dissmisable = dissmisable ?? !!close;
const { base, side: sideStyles } = alertStyles({
variant,
color,
size,
radius,
dissmisable: _dissmisable,
});
const Component = asChild ? Slot : "div";
if (!_open) return null;
return (
<AlertContextProvider
value={{
variant,
color,
size,
radius,
dissmisable,
}}
>
<Component
className={cn(base({ className }))}
role="alert"
{...rest}
ref={ref}
>
{isRenderable(side) && <div className={sideStyles()}>{side}</div>}
<div className="flex flex-col">
<Slottable>{children}</Slottable>
</div>
{_dissmisable && (
<button
type="button"
className="absolute size-8 top-0 end-0 inline-flex items-center justify-center text-white"
onClick={_close}
>
<X size={20} />
</button>
)}
</Component>
</AlertContextProvider>
);
});
Alert.displayName = alertDisplayName;
export const alertTitleDisplayName = "AlertTitle";
export type AlertTitleProps = HTMLAttributes<HTMLHeadingElement> & {
/**
* If true, the alert will be rendered as a child component.
*/
asChild?: boolean;
};
/**
* @name AlertTitle
* @description The AlertTitle component is used to display the title of the alert.
*/
export const AlertTitle = forwardRef<HTMLHeadingElement, AlertTitleProps>(
(props, ref) => {
const { asChild, className, children, ...rest } = props;
const context = useAlertContext();
const { alertTitle } = alertStyles(context);
const Component = asChild ? Slot : "h5";
return (
<Component
className={cn(alertTitle({ className }))}
role="heading"
{...rest}
ref={ref}
>
{children}
</Component>
);
}
);
AlertTitle.displayName = alertTitleDisplayName;
export const alertDescriptionDisplayName = "AlertDescription";
export type AlertDescriptionProps = HTMLAttributes<HTMLParagraphElement> & {
/**
* If true, the alert will be rendered as a child component.
*/
asChild?: boolean;
};
/**
* @name AlertDescription
* @description The AlertDescription component is used to display the description of the alert.
*/
export const AlertDescription = forwardRef<
HTMLParagraphElement,
AlertDescriptionProps
>((props, ref) => {
const { asChild, className, children, ...rest } = props;
const context = useAlertContext();
const { alertDescription } = alertStyles(context);
const Component = asChild ? Slot : "p";
return (
<Component
className={cn(alertDescription({ className }))}
{...rest}
ref={ref}
>
{children}
</Component>
);
});
AlertDescription.displayName = alertDescriptionDisplayName;
export const alertStyles = tv({
/**
* Base styles of the alert
*/
base: "relative flex items-start max-w-full overflow-hidden",
slots: {
alertTitle: "tracking-tight",
alertDescription: "",
side: "shrink-0 mt-px",
},
variants: {
/**
* Variant prop of the alert
*/
variant: {
filled: { base: "border" },
soft: {},
outline: { base: "border" },
},
/**
* Color prop of the alert
*/
color: {
blue: {},
red: {},
indigo: {},
green: {},
yellow: {},
zinc: {},
white: {},
black: {},
},
/**
* Size prop of the alert
*/
size: {
sm: {
base: "p-3 gap-2",
alertTitle: "text-base font-semibold mb-1 last:mb-0",
alertDescription: "text-sm mb-0.5 last:mb-0",
},
md: {
base: "p-4 gap-3",
alertTitle: "text-base font-semibold mb-1.5 last:mb-0",
alertDescription: "text-base mb-0.5 last:mb-0",
},
lg: {
base: "p-5 gap-4",
alertTitle: "text-lg font-semibold mb-2 last:mb-0",
alertDescription: "text-base mb-0.5 last:mb-0",
},
},
radius: {
none: { base: "rounded-none" },
sm: { base: "rounded-sm" },
md: { base: "rounded-md" },
lg: { base: "rounded-lg" },
xl: { base: "rounded-xl" },
},
dissmisable: {
true: {},
},
},
compoundVariants: [
/* -------------------------------------------------------------------------- */
/* Variants and colors */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* Filled variant */
/* -------------------------------------------------------------------------- */
{
variant: "filled",
color: "blue",
className: { base: "bg-blue-500 text-white hover:text-white" },
},
{
variant: "filled",
color: "red",
className: { base: "bg-red-500 text-white hover:text-white" },
},
{
variant: "filled",
color: "indigo",
className: { base: "bg-indigo-500 text-white hover:text-white" },
},
{
variant: "filled",
color: "green",
className: { base: "bg-green-500 text-white hover:text-white" },
},
{
variant: "filled",
color: "yellow",
className: { base: "bg-yellow-400 text-black hover:text-black" },
},
{
variant: "filled",
color: "zinc",
className: { base: "bg-zinc-500 text-white hover:text-white" },
},
{
variant: "filled",
color: "white",
className: { base: "bg-zinc-50 text-zinc-950 hover:text-zinc-950" },
},
{
variant: "filled",
color: "black",
className: {
base: "bg-zinc-950 border-zinc-900 hover:text-white",
alertTitle: "text-zinc-50",
alertDescription: "text-zinc-200",
side: "text-zinc-50",
},
},
/* -------------------------------------------------------------------------- */
/* Soft variant */
/* -------------------------------------------------------------------------- */
{
variant: "soft",
color: "blue",
className: { base: "bg-blue-100 text-blue-600 hover:text-blue-600" },
},
{
variant: "soft",
color: "red",
className: { base: "bg-red-100 text-red-600 hover:text-red-600" },
},
{
variant: "soft",
color: "indigo",
className: {
base: "bg-indigo-100 text-indigo-600 hover:text-indigo-600",
},
},
{
variant: "soft",
color: "green",
className: { base: "bg-green-100 text-green-600 hover:text-green-600" },
},
{
variant: "soft",
color: "yellow",
className: {
base: "bg-yellow-100 text-yellow-600 hover:text-yellow-600",
},
},
{
variant: "soft",
color: "zinc",
className: { base: "bg-zinc-100 text-zinc-600 hover:text-zinc-600" },
},
{
variant: "soft",
color: "white",
className: { base: "bg-zinc-50 text-zinc-950" },
},
{
variant: "soft",
color: "black",
className: {
base: "bg-zinc-200 text-zinc-600",
alertTitle: "text-zinc-950",
},
},
/* -------------------------------------------------------------------------- */
/* Outline variant */
/* -------------------------------------------------------------------------- */
{
variant: "outline",
color: "blue",
className: {
base: [
"text-blue-500 outline-blue-500",
"hover:bg-blue-500 hover:text-white",
"focus-visible:ring-offset-blue-300",
],
},
},
{
variant: "outline",
color: "red",
className: {
base: [
"text-red-500 outline-red-500",
"hover:bg-red-500 hover:text-white",
"focus-visible:ring-offset-red-300",
],
},
},
{
variant: "outline",
color: "indigo",
className: {
base: [
"text-indigo-500 outline-indigo-500",
"hover:bg-indigo-500 hover:text-white",
"focus-visible:ring-offset-indigo-300",
],
},
},
{
variant: "outline",
color: "green",
className: {
base: [
"text-green-500 outline-green-500",
"hover:bg-green-500 hover:text-white",
"focus-visible:ring-offset-green-300",
],
},
},
{
variant: "outline",
color: "yellow",
className: {
base: [
"text-yellow-600",
"hover:bg-yellow-500 hover:text-black",
"focus-visible:ring-offset-yellow-300",
],
},
},
{
variant: "outline",
color: "zinc",
className: {
base: [
"text-zinc-500",
"hover:bg-zinc-500 hover:text-white",
"focus-visible:ring-offset-zinc-300",
],
},
},
{
variant: "outline",
color: "white",
className: {
base: [""],
},
},
{
variant: "outline",
color: "black",
className: {
base: ["text-zinc-950"],
},
},
],
defaultVariants: {
variant: "filled",
color: "black",
size: "md",
radius: "lg",
},
});
The Alert component has the following props.
| Prop | Type | Default | Description |
|---|---|---|---|
asChild? | boolean | undefined | If true, the alert will be rendered as a child component. |
variant? | 'filled' | 'soft' | 'outline' | 'filled' | The variant of the alert. |
color? | 'blue' | 'red' | 'indigo' | 'green' | 'yellow' | 'zinc' | 'white' | 'black' | 'black' | The color of the alert. |
size? | 'sm' | 'md' | 'lg' | 'md' | The size of the alert. |
radius? | 'none' | 'sm' | 'md' | 'lg' | 'lg' | 'xl' | 'lg' | The border radius of the alert. |
isOpen? | boolean | undefined | Determines whether the alert is open or not. |
close? | () => void | undefined | Callback to close the alert. If provided, the alert will be dismissable. |
side? | React.ReactNode | undefined | The content to be displayed on the left side of the alert. |
dismissible? | boolean | undefined | If true, the alert will be dismissable. |