# Alert Inline Variants — React Notification Component

> Inline alerts in info, success, warning and error tones with an optional action; dismissing one blurs it out and the stack closes the gap on a spring.

- Category: [Notifications](https://pixelpapa.com/components/notifications.md)
- Tags: alert, inline, variants, dismiss, status
- Dependencies: lucide-react, motion (plus React and Tailwind CSS v4)
- Last updated: 2026-11-13
- Live preview: https://pixelpapa.com/components/notifications/alert-inline-variants

## Install

```bash
npx shadcn@latest add "https://pixelpapa.com/r/alert-inline-variants.json"
```

The file is written to `components/pixelpapa/alert-inline-variants.tsx`. To install manually, run `npm install lucide-react motion` and copy the source below into your project. It imports `cn` from `@/lib/utils`, which every shadcn project has.

## Source

```tsx
"use client";

import { CircleAlert, CircleCheck, Info, TriangleAlert, X, type LucideIcon } from "lucide-react";
import { AnimatePresence, motion, useReducedMotion } from "motion/react";
import { useState, type ReactNode } from "react";
import { cn } from "@/lib/utils";

export type AlertVariant = "info" | "success" | "warning" | "error";

const styles: Record<AlertVariant, { icon: LucideIcon; box: string; badge: string; live: "polite" | "assertive" }> = {
  info: { icon: Info, box: "border-primary/25 bg-primary/6", badge: "bg-primary/12 text-primary", live: "polite" },
  success: { icon: CircleCheck, box: "border-emerald-500/30 bg-emerald-500/8", badge: "bg-emerald-500/15 text-emerald-600 dark:text-emerald-400", live: "polite" },
  warning: { icon: TriangleAlert, box: "border-amber-500/35 bg-amber-500/10", badge: "bg-amber-500/20 text-amber-600 dark:text-amber-400", live: "polite" },
  error: { icon: CircleAlert, box: "border-destructive/30 bg-destructive/8", badge: "bg-destructive/12 text-destructive", live: "assertive" },
};

type AlertProps = {
  variant?: AlertVariant;
  title: string;
  children?: ReactNode;
  /** A link or button rendered under the text. */
  action?: ReactNode;
  /** Shows a close button when provided. */
  onDismiss?: () => void;
  className?: string;
};

export function Alert({ variant = "info", title, children, action, onDismiss, className }: AlertProps) {
  const reduce = useReducedMotion();
  const s = styles[variant];

  return (
    <motion.div
      layout
      role={variant === "error" ? "alert" : "status"}
      aria-live={s.live}
      initial={{ opacity: 0, y: reduce ? 0 : 12, scale: 0.98 }}
      animate={{ opacity: 1, y: 0, scale: 1, filter: "blur(0px)" }}
      exit={{ opacity: 0, scale: 0.96, filter: "blur(6px)", transition: { duration: 0.18 } }}
      transition={{ type: "spring", stiffness: 400, damping: 34 }}
      className={cn("flex items-start gap-3 rounded-2xl border p-3.5 text-foreground", s.box, className)}
    >
      <motion.span
        initial={{ scale: 0.4, rotate: -20 }}
        animate={{ scale: 1, rotate: 0 }}
        transition={{ type: "spring", stiffness: 500, damping: 18, delay: 0.1 }}
        className={cn("flex size-8 shrink-0 items-center justify-center rounded-xl", s.badge)}
      >
        <s.icon className="size-[18px]" />
      </motion.span>
      <div className="min-w-0 flex-1 pt-0.5">
        <p className="text-sm font-semibold">{title}</p>
        {children && <p className="mt-0.5 text-[13px] leading-relaxed text-muted-foreground">{children}</p>}
        {action && <div className="mt-2">{action}</div>}
      </div>
      {onDismiss && (
        <button
          aria-label={`Dismiss: ${title}`}
          onClick={onDismiss}
          className="flex size-7 shrink-0 items-center justify-center rounded-full text-muted-foreground transition-colors hover:bg-foreground/8 hover:text-foreground"
        >
          <X className="size-4" />
        </button>
      )}
    </motion.div>
  );
}

const alerts: { id: string; variant: AlertVariant; title: string; body: string; action?: string }[] = [
  { id: "info", variant: "info", title: "Scheduled maintenance on Sunday", body: "Exports pause for about 20 minutes from 02:00 UTC." },
  { id: "success", variant: "success", title: "Domain verified", body: "northwind.app now serves this project over HTTPS." },
  { id: "warning", variant: "warning", title: "You have used 92% of your bandwidth", body: "Traffic above 100 GB is billed at $0.12 per GB.", action: "Raise the limit" },
  { id: "error", variant: "error", title: "Deploy failed on main", body: "Build exited with code 1 after 41 seconds.", action: "View build logs" },
];

export default function AlertInlineVariantsDemo() {
  const [hidden, setHidden] = useState<string[]>([]);
  const visible = alerts.filter((a) => !hidden.includes(a.id));

  return (
    <div className="flex h-[430px] w-full max-w-md flex-col gap-2.5">
      <AnimatePresence initial={false}>
        {visible.map((a) => (
          <Alert
            key={a.id}
            variant={a.variant}
            title={a.title}
            onDismiss={() => setHidden([...hidden, a.id])}
            action={
              a.action && (
                <a href="#" onClick={(e) => e.preventDefault()} className="text-[13px] font-medium text-foreground underline decoration-foreground/30 underline-offset-4 hover:decoration-foreground">
                  {a.action}
                </a>
              )
            }
          >
            {a.body}
          </Alert>
        ))}
        {hidden.length > 0 && (
          <motion.button
            layout
            key="restore"
            initial={{ opacity: 0 }}
            animate={{ opacity: 1 }}
            exit={{ opacity: 0 }}
            onClick={() => setHidden([])}
            className="self-center rounded-full border border-border bg-card px-3.5 py-1.5 text-xs font-medium text-muted-foreground transition-colors hover:text-foreground"
          >
            Restore {hidden.length} dismissed
          </motion.button>
        )}
      </AnimatePresence>
    </div>
  );
}
```

## AI prompt

```text
Add the "Alert Inline Variants" component from PixelPapa UI to this project.

What it is: Inline alerts in info, success, warning and error tones with an optional action; dismissing one blurs it out and the stack closes the gap on a spring.
Category: Notifications. Depends on: lucide-react, motion.

1. Install it with the shadcn CLI:
   npx shadcn@latest add "https://pixelpapa.com/r/alert-inline-variants.json"
   (If the CLI is unavailable, fetch that URL, write files[0].content to components/pixelpapa/alert-inline-variants.tsx and install the listed dependencies.)
2. Import it from "@/components/pixelpapa/alert-inline-variants" and place it where I describe below.
3. Keep its animation timings and accessibility attributes. Replace the demo copy and data with content that fits my product, and use my existing theme tokens (bg-background, text-foreground, bg-primary, border-border) rather than hard-coded colours.

Where to use it: <describe the page and position here>
```

## More notifications

- [Notification Dynamic Island](https://pixelpapa.com/components/notifications/notification-dynamic-island.md) (Pro): A pill that springs between sizes to hold live activities (an incoming call, a timer, now playing) with content that blurs across each morph.
- [Notification Inbox Popover](https://pixelpapa.com/components/notifications/notification-inbox-popover.md) (Pro): A bell popover with Unread and All tabs: marking a row read drains its dot and folds it out of the unread list while the badge count rolls down.
- [Notification List Swipe](https://pixelpapa.com/components/notifications/notification-list-swipe.md): Notification rows you can swipe: right to archive, left to delete. The action underneath grows with the drag, and a flick or a long pull sends the row flying before the list closes up.
- [Toast Stack Sonner Like](https://pixelpapa.com/components/notifications/toast-stack-sonner-like.md) (Pro): A hand-built toaster: toasts pile into a tight stack, fan out into a readable list on hover, pause their timers while you look, and promise toasts morph from loading to done.
- [Toast Undo Progress](https://pixelpapa.com/components/notifications/toast-undo-progress.md): An undo toast with a draining countdown bar that pauses on hover; undo springs the removed row back into the list, and letting it run out commits the action.
