Callout
MDX-style admonition with info / success / warning / error / neutral variants and optional icon override. Ships --info / --success / --warning theme tokens.
Example
Installation
npx shadcn@latest add https://hirael.com/r/callout.jsonAPI
<Callout />
+ native element props| Prop | Type | Default |
|---|---|---|
variant | "success" | "warning" | "info" | "error" | "neutral" | null | "info" |
title | React.ReactNode | — |
icon | false | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | React.ElementType<any, keyof React.JSX.IntrinsicElements> | — |
Component source
import * as React from "react";
import {
AlertCircle,
AlertTriangle,
Ban,
CheckCircle2,
Info,
} from "lucide-react";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils";
const calloutVariants = cva(
"my-4 flex flex-col gap-2 overflow-hidden rounded-md border-s-4 p-4 text-sm",
{
// Status colors come from the --info / --success / --warning theme
// tokens (shipped with this component's cssVars) so callouts follow
// the consumer's theme in both modes — no hard-coded palette.
variants: {
variant: {
info: "border-info bg-info/10 text-info",
success: "border-success bg-success/10 text-success",
warning: "border-warning bg-warning/10 text-warning",
error: "border-destructive bg-destructive/10 text-destructive",
neutral: "border-muted-foreground/50 bg-muted/50 text-foreground",
},
},
defaultVariants: {
variant: "info",
},
},
);
type CalloutVariant = NonNullable<
VariantProps<typeof calloutVariants>["variant"]
>;
const variantIcons: Record<CalloutVariant, React.ElementType> = {
info: Info,
success: CheckCircle2,
warning: AlertTriangle,
error: Ban,
neutral: AlertCircle,
};
export type CalloutProps = Omit<React.ComponentProps<"div">, "title"> &
VariantProps<typeof calloutVariants> & {
title?: React.ReactNode;
icon?: React.ElementType | React.ReactElement | false;
};
function Callout({
title,
icon,
className,
variant = "info",
children,
...props
}: CalloutProps) {
const variantKey = (variant ?? "info") as CalloutVariant;
const renderIcon = () => {
if (icon === false) return null;
if (React.isValidElement(icon)) return icon;
const IconComponent =
(icon as React.ElementType | undefined) ?? variantIcons[variantKey];
return <IconComponent className="size-5 shrink-0" aria-hidden />;
};
return (
<div
data-slot="callout"
data-variant={variantKey}
className={cn(calloutVariants({ variant }), className)}
{...props}
>
{(title || icon !== false) && (
<div className="flex items-start gap-2">
{renderIcon()}
{title && <span className="font-semibold leading-5">{title}</span>}
</div>
)}
{children && (
<div className="text-sm leading-relaxed [&_a]:underline [&_a]:underline-offset-2">
{children}
</div>
)}
</div>
);
}
export { Callout, calloutVariants };
Dependencies
npm
lucide-reactclass-variance-authority