Skip to main content
Gremorie

Alert

Persistent in-flow message anchored to page content with an icon, title, and description.

Overview

Alert is the in-flow feedback primitive: a persistent message anchored inside page flow, distinct from Toast (transient, floating) and a page-spanning banner. Use it to surface information that the user needs to read in context before acting on the surrounding UI - a form-wide error summary, a tip about a configuration, a confirmation that an async write has just completed.

The Alert API stays deliberately small. Variants ship as default and destructive only - convey intent (info, success, warning, danger) through a leading icon from lucide-react rather than introducing more variants. This keeps the visual surface consistent and the API minimal.

Preview

'use client';import { Alert, AlertDescription, AlertTitle } from '@gremorie/rx-feedback';import { CheckCircle2, Info, AlertTriangle } from 'lucide-react';export function AlertPreview() {  return (    <div className="flex flex-col gap-3">      <Alert>        <Info className="size-4" />        <AlertTitle>Heads up</AlertTitle>        <AlertDescription>          The registry rebuilds on every commit to the main branch.        </AlertDescription>      </Alert>      <Alert variant="destructive">        <AlertTriangle className="size-4" />        <AlertTitle>Something went wrong</AlertTitle>        <AlertDescription>          Could not reach the upstream registry. Retry shortly.        </AlertDescription>      </Alert>      <Alert>        <CheckCircle2 className="size-4" />        <AlertTitle>Success</AlertTitle>        <AlertDescription>Primitive added to your project.</AlertDescription>      </Alert>    </div>  );}

Anatomy

Alert                  bordered container; pass an icon as the first child to claim the leading column
├─ <icon>              optional leading lucide-react glyph (Info, CheckCircle2, TriangleAlert, XCircle)
├─ AlertTitle          the headline
└─ AlertDescription    the body text

Installation

bash npx gremorie@latest add rx-alert
bash pnpm dlx gremorie@latest add rx-alert
bash yarn dlx gremorie@latest add rx-alert
bash bunx --bun gremorie@latest add rx-alert

Usage

import { Alert, AlertTitle, AlertDescription } from "@gremorie/rx-feedback";
import { Info } from "lucide-react";

export function RegistryNotice() {
  return (
    <Alert>
      <Info className="size-4" />
      <AlertTitle>Heads up</AlertTitle>
      <AlertDescription>
        The registry rebuilds on every commit to the main branch.
      </AlertDescription>
    </Alert>
  );
}
npx gremorie@latest add ng-alert
import { Component } from '@angular/core';
import { Alert, AlertTitle, AlertDescription } from '@gremorie/ng-feedback';

@Component({
  selector: 'app-example',
  standalone: true,
  imports: [Alert, AlertTitle, AlertDescription],
  template: `
    <gr-alert>
      <gr-alert-title>Heads up</gr-alert-title>
      <gr-alert-description>
        The registry rebuilds on every commit to the main branch.
      </gr-alert-description>
    </gr-alert>
  `,
})
export class ExampleComponent {}

API

<Alert>

The root. Renders a <div role="alert"> styled as a two-column grid: an optional 16 px icon slot on the left and a stack of title plus description on the right. The icon column collapses when no <svg> child is present.

PropTypeDefaultDescription
variant"default" | "destructive""default"Visual intent. default uses card surface tokens; destructive uses the destructive text color and tints the description.
classNamestring-Extra classes merged after the variant classes. Use for spacing escape hatches, not for changing the surface treatment.
...propsReact.ComponentProps<"div">-Standard div attributes. The role="alert" is set automatically; do not override it.

<AlertTitle>

Renders a <div> for the headline. Sits in the second column of the grid, line-clamped to one line by default so a long title cannot push the icon out of alignment.

PropTypeDefaultDescription
classNamestring-Extra classes merged after the title classes.
...propsReact.ComponentProps<"div">-Standard div attributes.

<AlertDescription>

Renders a <div> for the body. Inherits muted foreground on the default variant, switches to a tinted destructive color on the destructive variant via the data-slot selector.

PropTypeDefaultDescription
classNamestring-Extra classes merged after the description classes.
...propsReact.ComponentProps<"div">-Standard div attributes including nested <p> (which receives a relaxed leading by default).

Composition

  1. <Alert> is the root grid. Pick the variant first - destructive only when an action failed or a safety boundary is being crossed.
  2. An icon (any lucide-react glyph at className="size-4") is the first child. The grid auto-detects the SVG and reserves the icon column.
  3. <AlertTitle> is the headline. Keep it short (one line); the line-clamp prevents accidental wraps.
  4. <AlertDescription> is the body. One sentence is ideal; two paragraphs is the upper limit before the message belongs in a Dialog instead.

The grid auto-collapses to a single column when no icon is provided, so a stripped-down alert (title plus description only) still renders cleanly.

Variations

Alert ships exactly two CVA variants: default and destructive. There is no success, warning, or info variant. Express those intents through a leading lucide-react icon on the default surface, as shown below - this keeps the visual surface consistent and the API minimal.

Destructive

'use client';import { Alert, AlertDescription, AlertTitle } from '@gremorie/rx-feedback';import { XCircle } from 'lucide-react';export function AlertDestructivePreview() {  return (    <Alert variant="destructive">      <XCircle className="size-4" />      <AlertTitle>Payment failed</AlertTitle>      <AlertDescription>        Your card was declined. Update the payment method and try again.      </AlertDescription>    </Alert>  );}

The only non-default variant. Use for failed writes, broken integrations, and safety boundaries. The tinted description keeps the body readable while still signalling severity.

Intent via icon

'use client';import { Alert, AlertDescription, AlertTitle } from '@gremorie/rx-feedback';import { AlertTriangle, CheckCircle2, Info } from 'lucide-react';export function AlertWithIconPreview() {  return (    <div className="flex flex-col gap-3">      <Alert>        <Info className="size-4" />        <AlertTitle>Informational</AlertTitle>        <AlertDescription>          The registry rebuilds on every commit to the main branch.        </AlertDescription>      </Alert>      <Alert>        <CheckCircle2 className="size-4" />        <AlertTitle>Success</AlertTitle>        <AlertDescription>Primitive added to your project.</AlertDescription>      </Alert>      <Alert>        <AlertTriangle className="size-4" />        <AlertTitle>Warning</AlertTitle>        <AlertDescription>          This action rewrites tokens already imported by other components.        </AlertDescription>      </Alert>    </div>  );}

Informational, success, and warning intents all reuse the default variant and carry their meaning through the leading icon (Info, CheckCircle2, AlertTriangle). Reach for a dedicated colored surface only when you genuinely need destructive emphasis.

Title only

'use client';import { Alert, AlertTitle } from '@gremorie/rx-feedback';import { Info } from 'lucide-react';export function AlertTitleOnlyPreview() {  return (    <div className="flex flex-col gap-3">      <Alert>        <Info className="size-4" />        <AlertTitle>Changes saved automatically.</AlertTitle>      </Alert>      <Alert>        <AlertTitle>Read-only mode is on.</AlertTitle>      </Alert>    </div>  );}

Omit <AlertDescription> for a single-line status message. The grid collapses cleanly with or without a leading icon, so a terse confirmation reads as one compact row.

Accessibility

  • Role and announcement: the root carries role="alert", so assistive tech announces the message as it appears in the DOM. Mount alerts conditionally on the event that produced them, not eagerly at page load.
  • Title and description coupling: title and description live in the same grid cell so they announce as one chunk; no need to wire aria-describedby by hand.
  • Icons are decorative: the leading icon carries no semantic load. The intent is conveyed by the variant and the title - the icon merely reinforces it for sighted users. Leaf it as plain SVG (no aria-label).
  • Color is never the only channel: destructive intent ships with a dedicated title surface plus a recognizable icon, satisfying WCAG 1.4.1 Use of Color.
  • No motion injected: the primitive has no enter or exit animation. Pair with a motion wrapper if you want a fade or slide on mount.
  • Toast - reach for Toast for transient, floating confirmations that do not need to persist in flow.
  • Dialog - reach for Dialog when the user must respond before proceeding.
  • Skeleton - sibling feedback primitive for loading states.

On this page