---
title: AlertMessage
description: A short inline feedback message with warning, success and neutral statuses, two sizes, an optional border and an optional icon on either side of the label
---

```tsx
'use client';

import { IconsFixture } from '@clubmed/trident-icons/tests/IconsFixture';

import { AlertMessage } from '@/ui/AlertMessage';

export function AlertMessageDemo() {
  return (
    <IconsFixture>
      <div className="flex flex-col items-start gap-16">
        <AlertMessage status="warning" icon="Error">
          Alert message
        </AlertMessage>
        <AlertMessage status="success" icon="CheckDefault">
          Positive message
        </AlertMessage>
        <AlertMessage status="neutral" icon="Information">
          Information message
        </AlertMessage>
      </div>
    </IconsFixture>
  );
}
```

## Installation

Using the CLI:

```bash
npx shadcn@latest add https://develop.trident-ui.pro.clubmed/r/alert-message.json
```

Or manually, copy the following code into `/ui/AlertMessage.tsx`:

```tsx
import { Icon, type IconicNames } from '@clubmed/trident-icons';
import clsx from 'clsx';
import type { ComponentPropsWithoutRef, FunctionComponent } from 'react';

import { twMerge } from './helpers/twMerge';

export type AlertMessageStatus = 'warning' | 'success' | 'neutral';
export type AlertMessageSize = 'small' | 'large';
export type AlertMessageIconPosition = 'left' | 'right';

export interface AlertMessageProps extends ComponentPropsWithoutRef<'div'> {
  /**
   * Status of the message, drives the text, icon and border color
   */
  status?: AlertMessageStatus;
  /**
   * Size of the message: `small` (14px text, 20px icon) or `large` (16px text, 24px icon)
   */
  size?: AlertMessageSize;
  /**
   * Wrap the message in a rounded border matching the status color
   */
  bordered?: boolean;
  /**
   * Optional icon name from the Trident Icons library
   */
  icon?: IconicNames;
  /**
   * Side of the label where the icon is displayed
   */
  iconPosition?: AlertMessageIconPosition;
}

const STATUS_CLASSES: Record<AlertMessageStatus, string> = {
  warning: 'text-red',
  success: 'text-green',
  neutral: 'text-black',
};

export const AlertMessage: FunctionComponent<AlertMessageProps> = ({
  status = 'neutral',
  size = 'small',
  bordered = false,
  icon,
  iconPosition = 'left',
  className,
  children,
  ...attrs
}) => {
  const hasIcon = Boolean(icon);
  const isLarge = size === 'large';
  const iconNode = icon ? (
    <Icon name={icon} width={isLarge ? '24px' : '20px'} className="shrink-0" aria-hidden="true" />
  ) : null;

  return (
    <div
      role={status === 'warning' ? 'alert' : 'status'}
      {...attrs}
      data-name="AlertMessage"
      data-status={status}
      className={twMerge(
        clsx(
          'inline-flex items-start gap-8 font-sans font-semibold',
          STATUS_CLASSES[status],
          isLarge ? 'text-b3' : 'text-b4',
          {
            'rounded-16 [box-shadow:0_0_0_1px_inset_currentColor]': bordered,
            // Padding is 1px under the 12/20px spec to absorb the 1px border
            'p-12': bordered && !hasIcon,
            'py-12 ps-12 pe-20': bordered && hasIcon && iconPosition === 'left',
            'py-12 ps-20 pe-12': bordered && hasIcon && iconPosition === 'right',
          },
        ),
        className,
      )}
    >
      {iconPosition === 'left' && iconNode}
      <span>{children}</span>
      {iconPosition === 'right' && iconNode}
    </div>
  );
};
```

## Usage

```tsx showLineNumbers
import { AlertMessage } from '@/ui/AlertMessage';

export function Component() {
  return (
    <AlertMessage status="warning" icon="Error">
      This field is required
    </AlertMessage>
  );
}
```

## Examples

### Statuses

Each status applies a matching color to the text, the icon and (when `bordered`) the border:

| Status    | Color   | Suggested icon | Typical use                               |
| --------- | ------- | -------------- | ----------------------------------------- |
| `warning` | `red`   | `Error`        | Validation errors, blocking issues        |
| `success` | `green` | `CheckDefault` | Confirmation that an action succeeded     |
| `neutral` | `black` | `Information`  | Helper text, non-critical information     |

```tsx
'use client';

import { IconsFixture } from '@clubmed/trident-icons/tests/IconsFixture';

import { AlertMessage } from '@/ui/AlertMessage';

export function AlertMessageDemo() {
  return (
    <IconsFixture>
      <div className="flex flex-col items-start gap-16">
        <AlertMessage status="warning" icon="Error">
          Alert message
        </AlertMessage>
        <AlertMessage status="success" icon="CheckDefault">
          Positive message
        </AlertMessage>
        <AlertMessage status="neutral" icon="Information">
          Information message
        </AlertMessage>
      </div>
    </IconsFixture>
  );
}
```

### Sizes

`small` (default) uses the B4 text style (14px) with a 20px icon. `large` uses the B3 text style (16px) with a 24px icon.

```tsx
'use client';

import { IconsFixture } from '@clubmed/trident-icons/tests/IconsFixture';

import { AlertMessage } from '@/ui/AlertMessage';

export function AlertMessageSizesDemo() {
  return (
    <IconsFixture>
      <div className="flex flex-col items-start gap-16">
        <AlertMessage status="success" size="small" icon="CheckDefault">
          Small positive message
        </AlertMessage>
        <AlertMessage status="success" size="large" icon="CheckDefault">
          Large positive message
        </AlertMessage>
      </div>
    </IconsFixture>
  );
}
```

```tsx
<AlertMessage status="success" size="large" icon="CheckDefault">
  Large positive message
</AlertMessage>
```

### Bordered

Set `bordered` to wrap the message in a 16px-radius border in the status color. Spacing adapts to the icon: 12px all around without an icon, and 20px on the side opposite the icon when there is one. 

```tsx
'use client';

import { IconsFixture } from '@clubmed/trident-icons/tests/IconsFixture';

import { AlertMessage } from '@/ui/AlertMessage';

export function AlertMessageBorderedDemo() {
  return (
    <IconsFixture>
      <div className="flex flex-col items-start gap-16">
        <AlertMessage status="warning" bordered>
          Alert message
        </AlertMessage>
        <AlertMessage status="success" bordered icon="CheckDefault">
          Positive message
        </AlertMessage>
        <AlertMessage status="neutral" size="large" bordered icon="Information">
          Information message
        </AlertMessage>
      </div>
    </IconsFixture>
  );
}
```

```tsx
<AlertMessage status="neutral" size="large" bordered icon="Information">
  Information message
</AlertMessage>
```

### Icon position

The icon is rendered before the label by default. Use `iconPosition="right"` to render it after.

```tsx
'use client';

import { IconsFixture } from '@clubmed/trident-icons/tests/IconsFixture';

import { AlertMessage } from '@/ui/AlertMessage';

export function AlertMessageIconPositionDemo() {
  return (
    <IconsFixture>
      <div className="flex flex-col items-start gap-16">
        <AlertMessage status="warning" icon="Error" iconPosition="left">
          Icon on the left (default)
        </AlertMessage>
        <AlertMessage status="warning" icon="Error" iconPosition="right">
          Icon on the right
        </AlertMessage>
      </div>
    </IconsFixture>
  );
}
```

```tsx
<AlertMessage status="warning" icon="Error" iconPosition="right">
  Icon on the right
</AlertMessage>
```

## API Reference

### AlertMessage

| Prop           | Type                                    | Default     | Description                                                                                         |
| -------------- | --------------------------------------- | ----------- | --------------------------------------------------------------------------------------------------- |
| `status`       | `"warning" \| "success" \| "neutral"`   | `"neutral"` | Drives the text, icon and border color (`red`, `green`, `black`).                                   |
| `size`         | `"small" \| "large"`                    | `"small"`   | `small`: 14px text / 20px icon. `large`: 16px text / 24px icon.                                     |
| `bordered`     | `boolean`                               | `false`     | Wraps the message in a rounded border matching the status color.                                   |
| `icon`         | `IconicNames`                           | `undefined` | Optional icon from the Trident Icons library. No icon is rendered when omitted.                     |
| `iconPosition` | `"left" \| "right"`                     | `"left"`    | Side of the label where the icon is rendered.                                                       |
| `role`         | `AriaRole`                              | see below   | Defaults to `"alert"` for `warning` and `"status"` otherwise. Can be overridden.                   |
| `className`    | `string`                                | `undefined` | Extra classes for the container, merged with `tailwind-merge` so they can override built-in ones. |
| `children`     | `ReactNode`                             | `undefined` | The message content.                                                                                |

All other `div` attributes (`id`, `aria-*`, `data-*`, event handlers…) are forwarded to the container.

## Accessibility

- `warning` messages render with `role="alert"` so assistive technologies announce them immediately. Render them conditionally (when the error happens) rather than toggling their visibility, otherwise the announcement may not fire.
- `success` and `neutral` messages render with `role="status"`, which is announced politely without interrupting the user.
- The icon is decorative (`aria-hidden`); the label must carry the full meaning on its own. Color is never the only indicator.
- When the message describes a form field, link them with `aria-describedby` on the field pointing to the message `id`.

```tsx
<input id="email" aria-describedby="email-error" aria-invalid="true" />
<AlertMessage id="email-error" status="warning" icon="Error">
  Please enter a valid email address
</AlertMessage>
```

## Notes

- The component renders as `inline-flex`, so it only takes the width of its content. Add `className="flex"` (or `w-full`) to make it stretch.
- The icon is not chosen automatically from the status: pass `icon` explicitly. The suggested pairings are listed in the [Statuses](#statuses) table.
- Icons require an `IconsProvider` (or `IconsFixture` in demos/tests) higher in the tree, like every other component using `@clubmed/trident-icons`.
