Skip to main content
Nexus Design System
GitHub

Alert

Alert shows a status message inside the page. Use it for information, success, warning and error messages. Compose the icon, text, actions and close button as children.

  • Stack layout: use no actions, or one or two Button actions. Leave out AlertClose.
  • Inline layout: use AlertClose alone, one or two Button actions, or Buttons plus AlertClose.
  • AlertClose doesn't remove the alert. Remove it in your own onClick handler.
Information
This is an informational alert with an icon.
'use client';

import { InfoIcon } from '@phosphor-icons/react/ssr';

import {
  Alert,
  AlertDescription,
  AlertIcon,
  AlertTitle,
} from '@/components/nxds/components/alert/alert';

export default function AlertWithIcon() {
  return (
    <Alert
      layout="stack"
      presentation="card"
      variant="default"
      className="nx:max-w-md"
    >
      <AlertIcon>
        <InfoIcon />
      </AlertIcon>
      <AlertTitle>Information</AlertTitle>
      <AlertDescription>
        This is an informational alert with an icon.
      </AlertDescription>
    </Alert>
  );
}

Installation

npx nxds-cli@latest add alert

Run npx nxds-cli@latest init first if nxds isn’t set up in your app yet. See Install.

Usage

import {
  Alert,
  AlertActions,
  AlertClose,
  AlertContent,
  AlertDescription,
  AlertIcon,
  AlertTitle,
} from '@/components/nxds/components/alert/alert';

Composition

Alert
├── AlertIcon
├── AlertTitle
├── AlertDescription
├── AlertContent
│   ├── AlertTitle
│   └── AlertDescription
└── AlertActions
    └── AlertClose

Examples

Banner presentation

Show a full-width banner with square corners and a bottom border.

Information
New workspace invitations are available for review.
'use client';

import { InfoIcon } from '@phosphor-icons/react/ssr';

import {
  Alert,
  AlertDescription,
  AlertIcon,
  AlertTitle,
} from '@/components/nxds/components/alert/alert';

export default function AlertBannerPresentation() {
  return (
    <Alert
      layout="stack"
      presentation="banner"
      variant="information"
      className="nx:max-w-md"
    >
      <AlertIcon>
        <InfoIcon />
      </AlertIcon>
      <AlertTitle>Information</AlertTitle>
      <AlertDescription>
        New workspace invitations are available for review.
      </AlertDescription>
    </Alert>
  );
}

Dismissible close button

Remove the alert from state when the reader clicks AlertClose.

Invite ready
The workspace invitation can now be sent.
'use client';

import * as React from 'react';

import { InfoIcon } from '@phosphor-icons/react/ssr';

import {
  Alert,
  AlertActions,
  AlertClose,
  AlertContent,
  AlertDescription,
  AlertIcon,
  AlertTitle,
} from '@/components/nxds/components/alert/alert';

function DismissibleCloseButtonExample(
  props: React.ComponentProps<typeof Alert>
) {
  const [visible, setVisible] = React.useState(true);

  if (!visible) return <div />;

  return (
    <Alert {...props} className="nx:max-w-xl">
      <AlertIcon>
        <InfoIcon />
      </AlertIcon>
      <AlertContent>
        <AlertTitle>Invite ready</AlertTitle>
        <AlertDescription>
          The workspace invitation can now be sent.
        </AlertDescription>
      </AlertContent>
      <AlertActions>
        <AlertClose onClick={() => setVisible(false)} />
      </AlertActions>
    </Alert>
  );
}

export default function AlertDismissibleCloseButton() {
  return (
    <DismissibleCloseButtonExample
      layout="inline"
      presentation="card"
      variant="information"
    />
  );
}

Actions below description

Place two Button actions under the message in the stack layout.

Plan limit reached
Upgrade the workspace or remove unused seats before inviting more members.
'use client';

import { WarningIcon } from '@phosphor-icons/react/ssr';

import {
  Alert,
  AlertActions,
  AlertContent,
  AlertDescription,
  AlertIcon,
  AlertTitle,
} from '@/components/nxds/components/alert/alert';
import { Button } from '@/components/nxds/components/button/button';

export default function AlertActionsBelowDescription() {
  return (
    <Alert
      layout="stack"
      presentation="card"
      variant="warning"
      className="nx:max-w-md"
    >
      <AlertIcon>
        <WarningIcon />
      </AlertIcon>
      <AlertContent>
        <AlertTitle>Plan limit reached</AlertTitle>
        <AlertDescription>
          Upgrade the workspace or remove unused seats before inviting more
          members.
        </AlertDescription>
      </AlertContent>
      <AlertActions>
        <Button>Upgrade</Button>
        <Button variant="outline">View usage</Button>
      </AlertActions>
    </Alert>
  );
}

This example also needs: Button.

All variants

Compare the five status variants side by side.

Default
Default Alert
This is a default informational alert.
Information
Information Alert
This is an informational alert.
Destructive
Destructive Alert
This is a destructive/error alert.
Success
Success Alert
This is a success alert.
Warning
Warning Alert
This is a warning alert.
'use client';

import {
  CheckCircleIcon,
  InfoIcon,
  WarningCircleIcon,
  WarningIcon,
} from '@phosphor-icons/react/ssr';

import {
  Alert,
  AlertDescription,
  AlertIcon,
  AlertTitle,
} from '@/components/nxds/components/alert/alert';

export default function AlertAllVariants() {
  return (
    <div className="nx:flex nx:flex-col nx:gap-6">
      <div>
        <div className="nx:mb-4 nx:typography-label-default nx:text-foreground">
          Default
        </div>
        <Alert className="nx:max-w-md">
          <AlertIcon>
            <InfoIcon />
          </AlertIcon>
          <AlertTitle>Default Alert</AlertTitle>
          <AlertDescription>
            This is a default informational alert.
          </AlertDescription>
        </Alert>
      </div>

      <div>
        <div className="nx:mb-4 nx:typography-label-default nx:text-foreground">
          Information
        </div>
        <Alert variant="information" className="nx:max-w-md">
          <AlertIcon>
            <InfoIcon />
          </AlertIcon>
          <AlertTitle>Information Alert</AlertTitle>
          <AlertDescription>This is an informational alert.</AlertDescription>
        </Alert>
      </div>

      <div>
        <div className="nx:mb-4 nx:typography-label-default nx:text-foreground">
          Destructive
        </div>
        <Alert variant="destructive" className="nx:max-w-md">
          <AlertIcon>
            <WarningCircleIcon />
          </AlertIcon>
          <AlertTitle>Destructive Alert</AlertTitle>
          <AlertDescription>
            This is a destructive/error alert.
          </AlertDescription>
        </Alert>
      </div>

      <div>
        <div className="nx:mb-4 nx:typography-label-default nx:text-foreground">
          Success
        </div>
        <Alert variant="success" className="nx:max-w-md">
          <AlertIcon>
            <CheckCircleIcon />
          </AlertIcon>
          <AlertTitle>Success Alert</AlertTitle>
          <AlertDescription>This is a success alert.</AlertDescription>
        </Alert>
      </div>

      <div>
        <div className="nx:mb-4 nx:typography-label-default nx:text-foreground">
          Warning
        </div>
        <Alert variant="warning" className="nx:max-w-md">
          <AlertIcon>
            <WarningIcon />
          </AlertIcon>
          <AlertTitle>Warning Alert</AlertTitle>
          <AlertDescription>This is a warning alert.</AlertDescription>
        </Alert>
      </div>
    </div>
  );
}

Props

Alert

Alert props
PropTypeDefaultDescription
layout"inline" | "stack" | null"stack"No description
presentation"banner" | "card" | null"card"No description
variant"default" | "destructive" | "success" | "information" | "warning" | null"default"No description

AlertActions

AlertActions adds no props of its own; it accepts the props of the element or primitive it wraps.

AlertClose

AlertClose adds no props of its own; it accepts the props of the element or primitive it wraps.

AlertContent

AlertContent adds no props of its own; it accepts the props of the element or primitive it wraps.

AlertDescription

AlertDescription adds no props of its own; it accepts the props of the element or primitive it wraps.

AlertIcon

AlertIcon adds no props of its own; it accepts the props of the element or primitive it wraps.

AlertTitle

AlertTitle props
PropTypeDefaultDescription
asChildbooleanfalse

Applies the title styles to the child element, such as a real heading.

Alert shows a status message inside the page. Use it for information, success, warning and error messages. Compose the icon, text, actions and close button as children.

<script setup lang="ts">
import { PhInfo } from '@phosphor-icons/vue';

import {
  Alert,
  AlertDescription,
  AlertIcon,
  AlertTitle,
} from '@/components/alert';
</script>

<template>
  <Alert class="nx:max-w-md">
    <AlertIcon><PhInfo /></AlertIcon>
    <AlertTitle>Information</AlertTitle>
    <AlertDescription
      >This is an informational alert with an icon.</AlertDescription
    >
  </Alert>
</template>

Installation

  1. Install the following dependencies:

    npm install @phosphor-icons/vue@^2.2.1 class-variance-authority@^0.7.1 reka-ui@^2.11.0
    
  2. Copy and paste the following code into your project.

    components/alert/Alert.vue
    <script setup lang="ts">
    import type { HTMLAttributes } from 'vue';
    
    import { cn } from '../../lib/utils';
    
    import { type AlertVariants, alertVariants } from '.';
    
    export interface AlertProps {
      variant?: AlertVariants['variant'];
      presentation?: AlertVariants['presentation'];
      layout?: AlertVariants['layout'];
      class?: HTMLAttributes['class'];
    }
    
    const props = withDefaults(defineProps<AlertProps>(), {
      variant: 'default',
      presentation: 'card',
      layout: 'stack',
    });
    </script>
    
    <template>
      <div
        data-slot="alert"
        :data-variant="variant"
        :data-presentation="presentation"
        :data-layout="layout"
        :class="cn(alertVariants({ variant, presentation, layout }), props.class)"
      >
        <slot />
      </div>
    </template>
    
    components/alert/AlertActions.vue
    <script setup lang="ts">
    import type { HTMLAttributes } from 'vue';
    
    import { cn } from '../../lib/utils';
    
    export interface AlertActionsProps {
      class?: HTMLAttributes['class'];
    }
    
    const props = defineProps<AlertActionsProps>();
    </script>
    
    <template>
      <div
        data-slot="alert-actions"
        :class="
          cn(
            [
              'nx:mt-3 nx:flex nx:flex-wrap nx:items-center nx:gap-2',
              'nx:group-data-[layout=inline]/alert:mt-0 nx:group-data-[layout=inline]/alert:self-center',
            ],
            props.class
          )
        "
      >
        <slot />
      </div>
    </template>
    
    components/alert/AlertClose.vue
    <script setup lang="ts">
    import type { HTMLAttributes } from 'vue';
    
    import { IconX } from '../../lib/icons';
    import { cn } from '../../lib/utils';
    
    export interface AlertCloseProps {
      class?: HTMLAttributes['class'];
    }
    
    const props = defineProps<AlertCloseProps>();
    </script>
    
    <template>
      <button
        type="button"
        data-slot="alert-close"
        :class="
          cn(
            'nx:inline-flex nx:size-8 nx:shrink-0 nx:items-center nx:justify-center nx:rounded-sm nx:border-0 nx:bg-transparent nx:p-0 nx:text-muted-foreground',
            'nx:transition-control nx:hover:bg-container-hover nx:hover:text-foreground',
            'nx:focus-visible:bg-container-hover nx:focus-visible:text-foreground',
            'nx:focus-visible:outline-2 nx:focus-visible:outline-focus-default',
            'nx:disabled:pointer-events-none nx:disabled:text-disabled-foreground',
            'nx:[&_svg]:size-4 nx:[&_svg]:shrink-0',
            props.class
          )
        "
      >
        <slot>
          <IconX aria-hidden="true" />
          <span class="nx:sr-only">Dismiss alert</span>
        </slot>
      </button>
    </template>
    
    components/alert/AlertContent.vue
    <script setup lang="ts">
    import type { HTMLAttributes } from 'vue';
    
    import { cn } from '../../lib/utils';
    
    export interface AlertContentProps {
      class?: HTMLAttributes['class'];
    }
    
    const props = defineProps<AlertContentProps>();
    </script>
    
    <template>
      <div
        data-slot="alert-content"
        :class="cn('nx:flex nx:min-w-0 nx:flex-col', props.class)"
      >
        <slot />
      </div>
    </template>
    
    components/alert/AlertDescription.vue
    <script setup lang="ts">
    import type { HTMLAttributes } from 'vue';
    
    import { cn } from '../../lib/utils';
    
    export interface AlertDescriptionProps {
      class?: HTMLAttributes['class'];
    }
    
    const props = defineProps<AlertDescriptionProps>();
    </script>
    
    <template>
      <div
        data-slot="alert-description"
        :class="
          cn(
            'nx:typography-body-default nx:group-data-[variant=default]/alert:text-muted-foreground',
            props.class
          )
        "
      >
        <slot />
      </div>
    </template>
    
    components/alert/AlertIcon.vue
    <script setup lang="ts">
    import type { HTMLAttributes } from 'vue';
    
    import { cn } from '../../lib/utils';
    
    export interface AlertIconProps {
      class?: HTMLAttributes['class'];
    }
    
    const props = defineProps<AlertIconProps>();
    </script>
    
    <template>
      <span
        data-slot="alert-icon"
        aria-hidden="true"
        :class="
          cn(
            [
              'nx:flex nx:[&>svg]:size-4',
              'nx:group-data-[layout=stack]/alert:translate-y-0.5',
            ],
            props.class
          )
        "
      >
        <slot />
      </span>
    </template>
    
    components/alert/AlertTitle.vue
    <script setup lang="ts">
    import { Primitive, type PrimitiveProps } from 'reka-ui';
    import type { HTMLAttributes } from 'vue';
    
    import { cn } from '../../lib/utils';
    
    export interface AlertTitleProps extends PrimitiveProps {
      class?: HTMLAttributes['class'];
    }
    
    const props = withDefaults(defineProps<AlertTitleProps>(), { as: 'div' });
    </script>
    
    <template>
      <Primitive
        data-slot="alert-title"
        :as="as"
        :as-child="asChild"
        :class="
          cn(
            'nx:mt-0 nx:mb-1 nx:last:mb-0 nx:typography-label-default',
            props.class
          )
        "
      >
        <slot />
      </Primitive>
    </template>
    
    components/alert/index.ts
    import { cva, type VariantProps } from 'class-variance-authority';
    
    export { default as Alert, type AlertProps } from './Alert.vue';
    export {
      default as AlertActions,
      type AlertActionsProps,
    } from './AlertActions.vue';
    export { default as AlertClose, type AlertCloseProps } from './AlertClose.vue';
    export {
      default as AlertContent,
      type AlertContentProps,
    } from './AlertContent.vue';
    export {
      default as AlertDescription,
      type AlertDescriptionProps,
    } from './AlertDescription.vue';
    export { default as AlertIcon, type AlertIconProps } from './AlertIcon.vue';
    export { default as AlertTitle, type AlertTitleProps } from './AlertTitle.vue';
    
    export const alertVariants = cva(
      'nx:group/alert nx:grid nx:w-full nx:px-6 nx:py-4',
      {
        variants: {
          variant: {
            default: 'nx:border-border-default nx:bg-container nx:text-foreground',
            destructive:
              'nx:border-error-border nx:bg-error-subtle nx:text-error-subtle-foreground',
            success:
              'nx:border-success-border nx:bg-success-subtle nx:text-success-subtle-foreground',
            information:
              'nx:border-information-border nx:bg-information-subtle nx:text-information-subtle-foreground',
            warning:
              'nx:border-warning-border nx:bg-warning-subtle nx:text-warning-subtle-foreground',
          },
          presentation: {
            card: 'nx:rounded-md nx:border-default',
            banner: 'nx:rounded-none nx:border-b-default',
          },
          layout: {
            stack:
              'nx:grid-cols-[auto_minmax(0,1fr)] nx:items-start nx:has-[>[data-slot=alert-icon]]:gap-x-4 nx:*:data-[slot=alert-title]:col-start-2 nx:*:data-[slot=alert-description]:col-start-2 nx:*:data-[slot=alert-content]:col-start-2 nx:*:data-[slot=alert-actions]:col-start-2',
            inline:
              'nx:grid-cols-[minmax(0,1fr)_auto] nx:items-center nx:gap-x-4 nx:gap-y-1 nx:has-[>[data-slot=alert-icon]]:grid-cols-[auto_minmax(0,1fr)_auto]',
          },
        },
        defaultVariants: {
          variant: 'default',
          presentation: 'card',
          layout: 'stack',
        },
      }
    );
    
    export type AlertVariants = VariantProps<typeof alertVariants>;
    
    lib/icons.ts
    /**
     * Internal Icons
     *
     * This barrel file re-exports icons used internally by the design system.
     * These are hardcoded icons for component states (loading, validation, navigation).
     *
     * User-facing icons should be passed via props by consumers.
     *
     * This set draws Phosphor. `nxds init --icons` installs the same names drawn
     * by another library.
     */
    import { PhCircle } from '@phosphor-icons/vue';
    import { type FunctionalComponent, h } from 'vue';
    
    // Loading states
    export { PhCircleNotch as IconLoader2 } from '@phosphor-icons/vue';
    
    // Navigation
    export {
      PhArrowDown as IconArrowDown,
      PhArrowLeft as IconArrowLeft,
      PhArrowRight as IconArrowRight,
      PhArrowUp as IconArrowUp,
      PhCaretDown as IconChevronDown,
      PhCaretLeft as IconChevronLeft,
      PhCaretRight as IconChevronRight,
      PhCaretUp as IconChevronUp,
      PhDotsThree as IconDots,
      PhSidebarSimple as IconLayoutSidebar,
      PhCaretUpDown as IconSelector,
    } from '@phosphor-icons/vue';
    
    // Actions
    export {
      PhCheck as IconCheck,
      PhCopy as IconCopy,
      PhMinus as IconMinus,
      PhPencilSimple as IconPencil,
      PhPlus as IconPlus,
      PhX as IconX,
    } from '@phosphor-icons/vue';
    
    export const IconCircleFilled: FunctionalComponent = (_props, { attrs }) =>
      h(PhCircle, { weight: 'fill', ...attrs });
    
    // Filter field types
    export {
      PhCalendarBlank as IconCalendar,
      PhHash as IconHash,
      PhTextAa as IconLetterCase,
      PhListBullets as IconList,
    } from '@phosphor-icons/vue';
    
    // Search / input
    export { PhMagnifyingGlass as IconSearch } from '@phosphor-icons/vue';
    
    // Alerts/Status
    export {
      PhWarningCircle as IconAlertCircle,
      PhWarning as IconAlertTriangle,
      PhCheckCircle as IconCircleCheck,
      PhInfo as IconInfoCircle,
    } from '@phosphor-icons/vue';
    
    // Layout
    export {
      PhDotsSixVertical as IconGripVertical,
      PhPalette as IconPalette,
    } from '@phosphor-icons/vue';
    
  3. Keep each file at the path shown above it.

    Paths are relative to packages/vue/src/. Keep the same folders under your @/ root, and the imports between files work without edits.

Usage

import { Alert, AlertActions, AlertClose, AlertContent, AlertDescription, AlertIcon, AlertTitle } from '@/components/alert';

Examples

Banner presentation

Show a full-width banner with square corners and a bottom border.

<script setup lang="ts">
import { PhInfo } from '@phosphor-icons/vue';

import {
  Alert,
  AlertDescription,
  AlertIcon,
  AlertTitle,
} from '@/components/alert';
</script>

<template>
  <Alert variant="information" presentation="banner" class="nx:max-w-md">
    <AlertIcon><PhInfo /></AlertIcon>
    <AlertTitle>Information</AlertTitle>
    <AlertDescription
      >New workspace invitations are available for review.</AlertDescription
    >
  </Alert>
</template>

Dismissible close button

Remove the alert from state when the reader clicks AlertClose.

<script setup lang="ts">
import { PhInfo } from '@phosphor-icons/vue';
import { ref } from 'vue';

import {
  Alert,
  AlertActions,
  AlertClose,
  AlertContent,
  AlertDescription,
  AlertIcon,
  AlertTitle,
} from '@/components/alert';

const visible = ref(true);
</script>

<template>
  <Alert
    v-if="visible"
    variant="information"
    layout="inline"
    class="nx:max-w-xl"
  >
    <AlertIcon><PhInfo /></AlertIcon>
    <AlertContent>
      <AlertTitle>Invite ready</AlertTitle>
      <AlertDescription
        >The workspace invitation can now be sent.</AlertDescription
      >
    </AlertContent>
    <AlertActions>
      <AlertClose @click="visible = false" />
    </AlertActions>
  </Alert>
  <div v-else data-testid="dismissed-alert" />
</template>

Actions below description

Place two Button actions under the message in the stack layout.

<script setup lang="ts">
import { PhWarning } from '@phosphor-icons/vue';

import {
  Alert,
  AlertActions,
  AlertContent,
  AlertDescription,
  AlertIcon,
  AlertTitle,
} from '@/components/alert';
import { Button } from '@/components/button';
</script>

<template>
  <Alert variant="warning" class="nx:max-w-md">
    <AlertIcon><PhWarning /></AlertIcon>
    <AlertContent>
      <AlertTitle>Plan limit reached</AlertTitle>
      <AlertDescription
        >Upgrade the workspace or remove unused seats before inviting more
        members.</AlertDescription
      >
    </AlertContent>
    <AlertActions>
      <Button>Upgrade</Button>
      <Button variant="outline">View usage</Button>
    </AlertActions>
  </Alert>
</template>

This example also needs: Button.

All variants

Compare the five status variants side by side.

<script setup lang="ts">
import {
  PhCheckCircle,
  PhInfo,
  PhWarning,
  PhWarningCircle,
} from '@phosphor-icons/vue';

import {
  Alert,
  AlertDescription,
  AlertIcon,
  AlertTitle,
} from '@/components/alert';
</script>

<template>
  <div class="nx:flex nx:flex-col nx:gap-6">
    <div>
      <div class="nx:mb-4 nx:typography-label-default nx:text-foreground">
        Default
      </div>
      <Alert class="nx:max-w-md">
        <AlertIcon><PhInfo /></AlertIcon>
        <AlertTitle>Default Alert</AlertTitle>
        <AlertDescription
          >This is a default informational alert.</AlertDescription
        >
      </Alert>
    </div>
    <div>
      <div class="nx:mb-4 nx:typography-label-default nx:text-foreground">
        Information
      </div>
      <Alert variant="information" class="nx:max-w-md">
        <AlertIcon><PhInfo /></AlertIcon>
        <AlertTitle>Information Alert</AlertTitle>
        <AlertDescription>This is an informational alert.</AlertDescription>
      </Alert>
    </div>
    <div>
      <div class="nx:mb-4 nx:typography-label-default nx:text-foreground">
        Destructive
      </div>
      <Alert variant="destructive" class="nx:max-w-md">
        <AlertIcon><PhWarningCircle /></AlertIcon>
        <AlertTitle>Destructive Alert</AlertTitle>
        <AlertDescription>This is a destructive/error alert.</AlertDescription>
      </Alert>
    </div>
    <div>
      <div class="nx:mb-4 nx:typography-label-default nx:text-foreground">
        Success
      </div>
      <Alert variant="success" class="nx:max-w-md">
        <AlertIcon><PhCheckCircle /></AlertIcon>
        <AlertTitle>Success Alert</AlertTitle>
        <AlertDescription>This is a success alert.</AlertDescription>
      </Alert>
    </div>
    <div>
      <div class="nx:mb-4 nx:typography-label-default nx:text-foreground">
        Warning
      </div>
      <Alert variant="warning" class="nx:max-w-md">
        <AlertIcon><PhWarning /></AlertIcon>
        <AlertTitle>Warning Alert</AlertTitle>
        <AlertDescription>This is a warning alert.</AlertDescription>
      </Alert>
    </div>
  </div>
</template>

Props

Alert

Alert props
PropTypeDefaultDescription
layout"stack" | "inline" | null"stack"No description
presentation"card" | "banner" | null"card"No description
variant"default" | "destructive" | "success" | "information" | "warning" | null"default"No description

AlertActions

AlertActions adds no props of its own; it accepts the props of the element or primitive it wraps.

AlertClose

AlertClose adds no props of its own; it accepts the props of the element or primitive it wraps.

AlertContent

AlertContent adds no props of its own; it accepts the props of the element or primitive it wraps.

AlertDescription

AlertDescription adds no props of its own; it accepts the props of the element or primitive it wraps.

AlertIcon

AlertIcon adds no props of its own; it accepts the props of the element or primitive it wraps.

AlertTitle

AlertTitle adds no props of its own; it accepts the props of the element or primitive it wraps.