Siteplane UI

Alert

Present an inline message with a title and optional actions.

Markdown
Scheduled maintenance
Projects are read-only on Sunday from 02:00 to 03:00 UTC.
"use client";

import { InfoIcon } from "lucide-react";
import {
  Alert,
  AlertDescription,
  AlertTitle,
} from "@/siteplane/components/alert";

export default function Example() {
  return (
    <Alert className="max-w-md" role="status">
      <InfoIcon aria-hidden="true" />
      <AlertTitle>Scheduled maintenance</AlertTitle>
      <AlertDescription>
        Projects are read-only on Sunday from 02:00 to 03:00 UTC.
      </AlertDescription>
    </Alert>
  );
}

Installation

pnpm dlx shadcn@4.18.0 add @siteplane/alert

Complete the one-time setup first. Registry JSON lists source files and dependencies.

For this page’s examples, also install:

pnpm dlx shadcn@4.18.0 add @siteplane/button @siteplane/card

Usage

import { Alert, AlertTitle } from "@/siteplane/components/alert";

<Alert>
  <AlertTitle>Changes saved</AlertTitle>
</Alert>;

Composition

Alert
├── icon (optional)
├── AlertTitle
├── AlertDescription
├── AlertAction
└── AlertClose (optional)

Place the icon directly under Alert, before the title; the alert then reserves a column for it.

Variants

Choose a semantic variant, pair it with a matching icon and state the status in the text. The variant also sets the role. A default, info or success alert is a polite status, and an error or warning alert is an alert that a screen reader announces at once. Pass role to change it.

Comments are turned off
Members can view this project but cannot add comments.
New version available
Reload the page to get the latest features.
Payment received
Invoice INV-1042 has been paid. A receipt is on its way.
"use client";

import {
  CircleAlertIcon,
  CircleCheckIcon,
  InfoIcon,
  TriangleAlertIcon,
} from "lucide-react";
import {
  Alert,
  AlertDescription,
  AlertTitle,
} from "@/siteplane/components/alert";

const alerts = [
  {
    variant: "default",
    role: "status",
    icon: InfoIcon,
    title: "Comments are turned off",
    description: "Members can view this project but cannot add comments.",
  },
  {
    variant: "info",
    role: "status",
    icon: InfoIcon,
    title: "New version available",
    description: "Reload the page to get the latest features.",
  },
  {
    variant: "success",
    role: "status",
    icon: CircleCheckIcon,
    title: "Payment received",
    description: "Invoice INV-1042 has been paid. A receipt is on its way.",
  },
  {
    variant: "warning",
    role: "alert",
    icon: TriangleAlertIcon,
    title: "Storage almost full",
    description: "Your workspace has used 90% of its storage.",
  },
  {
    variant: "error",
    role: "alert",
    icon: CircleAlertIcon,
    title: "Payment failed",
    description: "Update your card to keep your subscription active.",
  },
] as const;

export default function Example() {
  return (
    <div className="grid w-full max-w-md gap-3">
      {alerts.map(({ variant, role, icon: Icon, title, description }) => (
        <Alert key={variant} role={role} variant={variant}>
          <Icon aria-hidden="true" />
          <AlertTitle>{title}</AlertTitle>
          <AlertDescription>{description}</AlertDescription>
        </Alert>
      ))}
    </div>
  );
}

With actions

Put buttons in AlertAction. They sit beside the message on larger screens and below it on small screens; connect their callbacks in your application.

Invite your team
Projects move faster when everyone can comment and review.
"use client";

import { InfoIcon } from "lucide-react";
import {
  Alert,
  AlertAction,
  AlertDescription,
  AlertTitle,
} from "@/siteplane/components/alert";
import { Button } from "@/siteplane/components/button";

export default function Example() {
  return (
    <Alert className="max-w-md" role="status" variant="info">
      <InfoIcon aria-hidden="true" />
      <AlertTitle>Invite your team</AlertTitle>
      <AlertDescription>
        Projects move faster when everyone can comment and review.
      </AlertDescription>
      <AlertAction>
        <Button size="sm" variant="ghost">
          Dismiss
        </Button>
        <Button size="sm">Invite members</Button>
      </AlertAction>
    </Alert>
  );
}

Page banner

Use layout="banner" for something that concerns the whole page, such as customers who can't book, at the top of the page above its header. The banner takes the tint of its tone across the full width, with a hairline below it, a short title and one short sentence. Only the icon carries the tone; the title stays in the primary text color.

The action of a banner is always a text link in the primary text color, underlined. The standard places it at the end of the row, in AlertAction (Button variant="link" profile="inline"). The icon, the text, the action and the close control stand on one row while the banner's own width allows it. Below 32rem of its own width, the icon and the close control sit on the title's line, the text runs between them and the action goes under the text.

"use client";

import { CircleAlertIcon, TriangleAlertIcon } from "lucide-react";
import {
  Alert,
  AlertAction,
  AlertClose,
  AlertDescription,
  AlertTitle,
} from "@/siteplane/components/alert";
import { Button } from "@/siteplane/components/button";

export default function Example() {
  return (
    <div className="w-full">
      <Alert layout="banner" variant="error">
        <CircleAlertIcon aria-hidden="true" />
        <AlertTitle>Customers can't book since Oct 12</AlertTitle>
        <AlertDescription>
          Stripe needs details from the business to take payments.
        </AlertDescription>
        <AlertAction>
          <Button
            profile="inline"
            render={<a href="#booking" />}
            variant="link"
          >
            Open booking
          </Button>
        </AlertAction>
        <AlertClose />
      </Alert>
      <Alert layout="banner" variant="warning">
        <TriangleAlertIcon aria-hidden="true" />
        <AlertTitle>Changes show up to a minute late</AlertTitle>
        <AlertDescription>
          Check the website's address in Settings.
        </AlertDescription>
        <AlertAction>
          <Button
            profile="inline"
            render={<a href="#settings" />}
            variant="link"
          >
            Open Settings
          </Button>
        </AlertAction>
        <AlertClose />
      </Alert>
    </div>
  );
}

The same link can also end the sentence, so the banner stays two lines high. The link keeps its words together. A text action never reads like a red delete button, also on an error banner.

"use client";

import { CircleAlertIcon, TriangleAlertIcon } from "lucide-react";
import {
  Alert,
  AlertClose,
  AlertDescription,
  AlertTitle,
} from "@/siteplane/components/alert";
import { Button } from "@/siteplane/components/button";

export default function Example() {
  return (
    <div className="w-full">
      <Alert layout="banner" variant="error">
        <CircleAlertIcon aria-hidden="true" />
        <AlertTitle>Customers can't book since Oct 12</AlertTitle>
        <AlertDescription>
          <p>
            Stripe needs details from the business to take payments.{" "}
            <Button
              profile="inline"
              render={<a href="#booking" />}
              variant="link"
            >
              Open booking
            </Button>
          </p>
        </AlertDescription>
        <AlertClose />
      </Alert>
      <Alert layout="banner" variant="warning">
        <TriangleAlertIcon aria-hidden="true" />
        <AlertTitle>Changes show up to a minute late</AlertTitle>
        <AlertDescription>
          <p>
            Check the website's address in Settings.{" "}
            <Button
              profile="inline"
              render={<a href="#settings" />}
              variant="link"
            >
              Open Settings
            </Button>
          </p>
        </AlertDescription>
        <AlertClose />
      </Alert>
    </div>
  );
}

Card band

Use layout="band" for a problem that belongs to one card. Place the Alert directly under the CardHeader: it runs across the card's full width, starts its icon where the card's text starts and lays out by its own width, not the window's. It takes the action at the end or the link in the text, like a banner.

Bookings
Requests from the website, newest first.
12 requests this week
"use client";

import { TriangleAlertIcon } from "lucide-react";
import {
  Alert,
  AlertAction,
  AlertClose,
  AlertDescription,
  AlertTitle,
} from "@/siteplane/components/alert";
import { Button } from "@/siteplane/components/button";
import {
  Card,
  CardDescription,
  CardHeader,
  CardPanel,
  CardTitle,
} from "@/siteplane/components/card";

export default function Example() {
  return (
    <Card className="w-full max-w-xl">
      <CardHeader>
        <CardTitle>Bookings</CardTitle>
        <CardDescription>
          Requests from the website, newest first.
        </CardDescription>
      </CardHeader>
      <Alert layout="band" variant="warning">
        <TriangleAlertIcon aria-hidden="true" />
        <AlertTitle>Two requests wait for a reply</AlertTitle>
        <AlertDescription>Guests hear back within a day.</AlertDescription>
        <AlertAction>
          <Button
            profile="inline"
            render={<a href="#requests" />}
            variant="link"
          >
            Answer now
          </Button>
        </AlertAction>
        <AlertClose />
      </Alert>
      <CardPanel className="text-sm">12 requests this week</CardPanel>
    </Card>
  );
}

Closing

AlertClose adds the kit's ghost close control, labelled "Close". It closes the Alert, which glides up and fades out with the kit's motion while the content below moves up; with reduced motion it closes at once. When focus was inside the Alert, it moves to the next element after it, never to the page. The Alert closes by itself, or set open and onOpenChange to decide in your application how long it stays closed. An inline Alert with AlertClose needs defaultOpen or open to collapse with motion.

API reference

Component properties
PropTypeDefaultDescription
Alert.variantdefault | info | success | warning | errordefaultColor treatment.
Alert.layoutinline | banner | bandinlineA surface in the content flow, a band across the top of a page, or a band at the top of a Card.
Alert.openbooleanunsetShows the Alert, controlled. Closing collapses it with the kit's motion.
Alert.defaultOpenbooleantrueShows the Alert at first, uncontrolled.
Alert.onOpenChange(open: boolean) => voidunsetCalled with false when AlertClose closes the Alert.
AlertClose.aria-labelstringCloseThe accessible name of the close control.
Alert.roleARIA rolealert for error and warning, status otherwiseDefault, info and success are a polite status. Pass a role to override it.

The four text and action parts accept native div attributes, refs, children and string className, and AlertClose accepts native button attributes. Alert owns the surface, the grid and the open state; AlertTitle is the emphasized text; AlertDescription lays out supporting content; AlertAction places actions beside the message on larger screens and below on smaller screens; AlertClose closes the Alert. Titles are divs, not semantic headings. There is no timer.

Read the installed source.

Usage notes

Reserve assertive alert announcements for urgent changes. Keep a clear recovery action for errors, and do not announce all static informational messages as alerts.