---
name: alert
title: Alert
url: https://ui.frontic.com/components/ui/alert.md
tier: ui
install: npx @frontic/ui add alert
files:
  - components/ui/alert/Alert.vue
  - components/ui/alert/AlertAction.vue
  - components/ui/alert/AlertDescription.vue
  - components/ui/alert/AlertTitle.vue
  - components/ui/alert/context.ts
  - components/ui/alert/index.ts
dependencies:
  - "@lucide/vue"
  - class-variance-authority
  - reka-ui
---

# Alert

A short message in neutral, info, success, warning or error, with an optional icon, title, description and an action such as dismiss.

## Install

```sh
npx @frontic/ui add alert
```

Used by: [Account Access](https://ui.frontic.com/components/sections/account-access.md), [Delivery Slot](https://ui.frontic.com/components/sections/delivery-slot.md), [Promo Code](https://ui.frontic.com/components/sections/promo-code.md), [Checkout Page](https://ui.frontic.com/components/templates/checkout-page.md)

npm packages: `@lucide/vue`, `class-variance-authority`, `reka-ui`

## Examples

```vue
<script setup lang="ts">
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'
</script>

<template>
  <Alert class="w-full max-w-lg">
    <AlertTitle>Order placed</AlertTitle>
    <AlertDescription>We sent the confirmation to your inbox.</AlertDescription>
  </Alert>
</template>
```

## Anatomy

```vue
<Alert>
  <AlertTitle />
  <AlertDescription />
</Alert>
```

## API reference

### Alert

Shows a short message to the shopper, with an optional icon, title and description. Renders a `<div>`.

`data-slot="alert"` · `Alert.vue`

Styling: `alert`, which extends [`notice-info`](https://ui.frontic.com/DESIGN.md#notice-info), [`notice-positive`](https://ui.frontic.com/DESIGN.md#notice-positive), [`notice-warning`](https://ui.frontic.com/DESIGN.md#notice-warning) and [`notice-destructive`](https://ui.frontic.com/DESIGN.md#notice-destructive). Change them there and every part that extends them follows.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |
| `variant` | `AlertVariant`: `'neutral' \| 'info' \| 'success' \| 'warning' \| 'error'` | `'neutral'` | The state the message reports: neutral, info, success, warning or error. Each takes its colour and its icon. |
| `icon` | `boolean` | `true` | Shows the state's icon before the content; the `icon` slot replaces it. |

| Slot | Slot props | Description |
| --- | --- | --- |
| `icon` |  | Replaces the state's icon. |

| Data attribute | Values | Description |
| --- | --- | --- |
| `data-variant` | `'neutral' \| 'info' \| 'success' \| 'warning' \| 'error'` | Carries the `variant` prop. |

### AlertAction

Holds a button or link that lets the user act on or dismiss the alert. Renders a `<div>`.

`data-slot="alert-action"` · `AlertAction.vue`

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `asChild` | `boolean` | `false` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
| `as` | `AsTag \| Component`: `ComponentOptions<any, any, any, ComputedOptions, MethodOptions, any, any, any, string, {}, {}, string, {}, {}, {}, string, ComponentProvideOptions> \| (props: any, ctx: Omit<{ attrs: Attrs; slots: Readonly<InternalSlots>; emit: (event: string, ...args: any[]) => void; expose: <Exposed extends Record<string, any> = Record<string, any>>(exposed?: Exposed \| undefined) => void; }, 'expose'>): any \| ComponentPublicInstanceConstructor<any, any, any, any, ComputedOptions, MethodOptions> \| 'a' \| 'button' \| 'div' \| 'form' \| 'h2' \| 'h3' \| 'img' \| 'input' \| 'label' \| 'li' \| 'nav' \| 'ol' \| 'p' \| 'span' \| 'svg' \| 'ul' \| 'template' \| {} & string` | `'div'` | The element or component this component should render as. Can be overwritten by `asChild`. |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |

| Data attribute | Values | Description |
| --- | --- | --- |
| `data-alert-action` |  | Marks the alert's action, so the alert leaves room for it. |

### AlertDescription

Shows the detail text of the alert below its title. Renders a `<div>`.

`data-slot="alert-description"` · `AlertDescription.vue`

Styling: `alert-description`, which extends [`part-description`](https://ui.frontic.com/DESIGN.md#part-description) and [`link-foreground`](https://ui.frontic.com/DESIGN.md#link-foreground). Change them there and every part that extends them follows.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `asChild` | `boolean` | `false` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
| `as` | `AsTag \| Component`: `ComponentOptions<any, any, any, ComputedOptions, MethodOptions, any, any, any, string, {}, {}, string, {}, {}, {}, string, ComponentProvideOptions> \| (props: any, ctx: Omit<{ attrs: Attrs; slots: Readonly<InternalSlots>; emit: (event: string, ...args: any[]) => void; expose: <Exposed extends Record<string, any> = Record<string, any>>(exposed?: Exposed \| undefined) => void; }, 'expose'>): any \| ComponentPublicInstanceConstructor<any, any, any, any, ComputedOptions, MethodOptions> \| 'a' \| 'button' \| 'div' \| 'form' \| 'h2' \| 'h3' \| 'img' \| 'input' \| 'label' \| 'li' \| 'nav' \| 'ol' \| 'p' \| 'span' \| 'svg' \| 'ul' \| 'template' \| {} & string` | `'div'` | The element or component this component should render as. Can be overwritten by `asChild`. |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |

### AlertTitle

Shows the headline of the alert. Renders a `<div>`.

`data-slot="alert-title"` · `AlertTitle.vue`

Styling: `alert-title`, which extends [`link-foreground`](https://ui.frontic.com/DESIGN.md#link-foreground). Change it there and every part that extends it follows.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `asChild` | `boolean` | `false` | Change the default rendered element for the one passed as a child, merging their props and behavior.<br><br>Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
| `as` | `AsTag \| Component`: `ComponentOptions<any, any, any, ComputedOptions, MethodOptions, any, any, any, string, {}, {}, string, {}, {}, {}, string, ComponentProvideOptions> \| (props: any, ctx: Omit<{ attrs: Attrs; slots: Readonly<InternalSlots>; emit: (event: string, ...args: any[]) => void; expose: <Exposed extends Record<string, any> = Record<string, any>>(exposed?: Exposed \| undefined) => void; }, 'expose'>): any \| ComponentPublicInstanceConstructor<any, any, any, any, ComputedOptions, MethodOptions> \| 'a' \| 'button' \| 'div' \| 'form' \| 'h2' \| 'h3' \| 'img' \| 'input' \| 'label' \| 'li' \| 'nav' \| 'ol' \| 'p' \| 'span' \| 'svg' \| 'ul' \| 'template' \| {} & string` | `'div'` | The element or component this component should render as. Can be overwritten by `asChild`. |
| `class` | `ClassValue` |  | Classes added to the part's own element, after its recipe classes. |

## Exports

```ts
import { type AlertVariant } from '@/components/ui/alert'
```

- `AlertVariant`: `'neutral' \| 'info' \| 'success' \| 'warning' \| 'error'`

## Styling

| Part | Recipe |
| --- | --- |
| `default` | [`alert`](https://ui.frontic.com/DESIGN.md#alert) |
| `description` | [`alert-description`](https://ui.frontic.com/DESIGN.md#alert-description) |
| `title` | [`alert-title`](https://ui.frontic.com/DESIGN.md#alert-title) |

## Source

`components/ui/alert/Alert.vue`

```vue
<script lang="ts">
/** Shows a short message to the shopper, with an optional icon, title and description. */
export default {}
</script>

<script setup lang="ts">
import { InfoIcon, CircleCheckIcon, TriangleAlertIcon, OctagonXIcon, BellRingIcon } from '@lucide/vue'

import type { HTMLAttributes } from 'vue'
import type { AlertVariant } from './context'
import { cva } from 'class-variance-authority'
import { computed, provide } from 'vue'
import { ALERT_VARIANT } from './context'

const alert = cva(
  'fui-alert group/alert relative *:col-start-2 *:data-icon:col-start-1 *:data-icon:row-span-2 *:data-icon:me-1.5 *:data-icon:translate-y-0.5 after:absolute after:-inset-y-px after:-inset-s-px',
  {
    variants: {
      variant: {
        neutral: 'fui-alert-neutral',
        info: 'fui-alert-info',
        success: 'fui-alert-success',
        warning: 'fui-alert-warning',
        error: 'fui-alert-error',
      },
    },
    defaultVariants: { variant: 'neutral' },
  },
)

const props = withDefaults(
  defineProps<{
    class?: HTMLAttributes['class']
    /** The state the message reports: neutral, info, success, warning or error. Each takes its colour and its icon. */
    variant?: AlertVariant
    /** Shows the state's icon before the content; the `icon` slot replaces it. */
    icon?: boolean
  }>(),
  { variant: 'neutral', icon: true },
)

defineSlots<{
  /** Replaces the state's icon. */
  icon?: () => unknown
  /** The title, description and actions. */
  default?: () => unknown
}>()

provide(
  ALERT_VARIANT,
  computed(() => props.variant),
)
</script>

<template>
  <div data-slot="alert" :data-variant="props.variant" :class="[alert({ variant: props.variant }), props.class]" role="alert">
    <slot v-if="props.icon" name="icon">
      <InfoIcon v-if="props.variant === 'info'" data-icon />
      <CircleCheckIcon v-else-if="props.variant === 'success'" data-icon />
      <TriangleAlertIcon v-else-if="props.variant === 'warning'" data-icon />
      <OctagonXIcon v-else-if="props.variant === 'error'" data-icon />
      <BellRingIcon v-else data-icon />
    </slot>
    <slot />
  </div>
</template>
```

`components/ui/alert/AlertAction.vue`

```vue
<script lang="ts">
/** Holds a button or link that lets the user act on or dismiss the alert. */
export default {}
</script>

<script setup lang="ts">
import type { PrimitiveProps } from 'reka-ui'
import { Primitive } from 'reka-ui'
import type { HTMLAttributes } from 'vue'

const props = withDefaults(
  defineProps<
    PrimitiveProps & {
      class?: HTMLAttributes['class']
    }
  >(),
  { as: 'div' },
)
</script>

<template>
  <Primitive :as="props.as" :as-child="props.asChild" data-slot="alert-action" data-alert-action :class="['absolute top-2.5 inset-e-3', props.class]">
    <slot />
  </Primitive>
</template>
```

`components/ui/alert/AlertDescription.vue`

```vue
<script lang="ts">
/** Shows the detail text of the alert below its title. */
export default {}
</script>

<script setup lang="ts">
import type { PrimitiveProps } from 'reka-ui'
import { Primitive } from 'reka-ui'
import type { HTMLAttributes } from 'vue'
import { cva } from 'class-variance-authority'
import { computed, inject } from 'vue'
import { ALERT_VARIANT } from './context'

const props = withDefaults(
  defineProps<
    PrimitiveProps & {
      class?: HTMLAttributes['class']
    }
  >(),
  { as: 'div' },
)

const alertDescription = cva('fui-alert-description text-balance group-has-data-alert-action/alert:me-18 [&_p:not(:last-child)]:mb-4', {
  variants: {
    variant: {
      neutral: 'fui-alert-description-neutral',
      error: 'fui-alert-description-error',
    },
  },
  defaultVariants: { variant: 'neutral' },
})

const variant = inject(
  ALERT_VARIANT,
  computed(() => 'neutral' as const),
)
</script>

<template>
  <Primitive :as="props.as" :as-child="props.asChild" data-slot="alert-description" :class="[alertDescription({ variant: variant === 'error' ? 'error' : 'neutral' }), props.class]">
    <slot />
  </Primitive>
</template>
```

`components/ui/alert/AlertTitle.vue`

```vue
<script lang="ts">
/** Shows the headline of the alert. */
export default {}
</script>

<script setup lang="ts">
import type { PrimitiveProps } from 'reka-ui'
import { Primitive } from 'reka-ui'
import type { HTMLAttributes } from 'vue'

const props = withDefaults(
  defineProps<
    PrimitiveProps & {
      class?: HTMLAttributes['class']
    }
  >(),
  { as: 'div' },
)
</script>

<template>
  <Primitive :as="props.as" :as-child="props.asChild" data-slot="alert-title" :class="['fui-alert-title group-has-data-alert-action/alert:me-18', props.class]">
    <slot />
  </Primitive>
</template>
```

`components/ui/alert/context.ts`

```ts
import type { ComputedRef, InjectionKey } from 'vue'

export type AlertVariant = 'neutral' | 'info' | 'success' | 'warning' | 'error'

export const ALERT_VARIANT: InjectionKey<ComputedRef<AlertVariant>> = Symbol('alert-variant')
```
