> ## Documentation Index
> Fetch the complete documentation index at: https://www.beeq.design/llms.txt
> Use this file to discover all available pages before exploring further.

# Notification

> Notifications deliver important information to users in a non-intrusive way, surfacing system messages, status updates, and alerts without interrupting the current flow.

export const CodeLivePreview = ({code, children, height, removePadding = false, mode = 'shadow'}) => {
  const previewRef = useRef(null);
  const BEEQ_ESM_URL = 'https://esm.sh/@beeq/core/dist/beeq/beeq.esm.js';
  const BEEQ_CSS_URL = 'https://esm.sh/@beeq/core/dist/beeq/beeq.css';
  const BEEQ_ICONS_URL = 'https://esm.sh/@beeq/core/dist/beeq/svg';
  const ensureParentBeeqRuntime = () => {
    if (document.querySelector('script[data-beeq-parent-runtime]')) return;
    const script = document.createElement('script');
    script.type = 'module';
    script.src = BEEQ_ESM_URL;
    script.dataset.beeqParentRuntime = BEEQ_ICONS_URL;
    document.head.appendChild(script);
  };
  const executeSnippetScripts = (root, runtimeWindow, previewRootArg, skipAttr) => {
    root.querySelectorAll('script').forEach(oldScript => {
      if (skipAttr && oldScript.hasAttribute(skipAttr)) return;
      if (oldScript.src) {
        const newScript = runtimeWindow.document.createElement('script');
        newScript.src = oldScript.src;
        oldScript.replaceWith(newScript);
        return;
      }
      runtimeWindow.Function('previewRoot', oldScript.textContent || '')(previewRootArg);
      oldScript.remove();
    });
  };
  const syncIframeMode = iframeDoc => {
    const root = document.documentElement;
    const explicitMode = root.getAttribute('bq-mode');
    const isDark = root.classList.contains('dark');
    const resolvedMode = explicitMode || (isDark ? 'dark' : 'light');
    iframeDoc.documentElement.setAttribute('bq-mode', resolvedMode);
    iframeDoc.body.setAttribute('bq-mode', resolvedMode);
  };
  useEffect(() => {
    if (mode === 'shadow') ensureParentBeeqRuntime();
  }, [mode]);
  useEffect(() => {
    const container = previewRef.current;
    if (!container) return;
    if (removePadding) container.style.padding = '0';
    container.innerHTML = '';
    if (mode === 'iframe') {
      const iframe = document.createElement('iframe');
      iframe.className = 'preview-iframe';
      iframe.style.width = '100%';
      iframe.style.border = '0';
      iframe.style.display = 'block';
      iframe.style.minHeight = height ?? '0px';
      iframe.setAttribute('title', 'Live code preview');
      iframe.setAttribute('loading', 'lazy');
      iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin');
      container.appendChild(iframe);
      const iframeDoc = iframe.contentDocument;
      const iframeWin = iframe.contentWindow;
      if (!iframeDoc || !iframeWin) return;
      iframeDoc.open();
      iframeDoc.write(['<!doctype html>', '<html>', '<head>', '  <meta charset="utf-8" />', '  <meta name="viewport" content="width=device-width, initial-scale=1" />', '  <style>html,body{margin:0;padding:0;min-height:100%;}</style>', `  <link rel="stylesheet" href="${BEEQ_CSS_URL}" />`, `  <script type="module" src="${BEEQ_ESM_URL}" data-beeq-iframe-runtime="${BEEQ_ICONS_URL}"></script>`, '</head>', '<body>', code, '</body>', '</html>'].join('\n'));
      iframeDoc.close();
      syncIframeMode(iframeDoc);
      const modeObserver = new MutationObserver(() => syncIframeMode(iframeDoc));
      modeObserver.observe(document.documentElement, {
        attributes: true,
        attributeFilter: ['class', 'bq-mode']
      });
      executeSnippetScripts(iframeDoc, iframeWin, iframeDoc, 'data-beeq-iframe-runtime');
      return () => modeObserver.disconnect();
    }
    const shadowRoot = container.shadowRoot ?? container.attachShadow({
      mode: 'open'
    });
    if (!shadowRoot.adoptedStyleSheets.length) {
      const fixSheet = new CSSStyleSheet();
      fixSheet.replaceSync(`
        bq-dropdown::part(panel),
        bq-panel::part(panel),
        bq-select::part(panel),
        bq-date-picker::part(panel) { z-index: 9999; }
        bq-tooltip::part(panel) { position: absolute; }
      `);
      shadowRoot.adoptedStyleSheets = [fixSheet];
    }
    shadowRoot.innerHTML = [`<link rel="stylesheet" href="${BEEQ_CSS_URL}">`, code].join('');
    executeSnippetScripts(shadowRoot, window, shadowRoot);
    return undefined;
  }, [code, mode, height]);
  return <div className="code-live-preview not-prose">
      {}
      <div className="preview" ref={previewRef} style={{
    minHeight: height
  }} />

      {}
      {children && <div className="code">{children}</div>}
    </div>;
};

export const CardTile = ({title, href, imageLightSrc, imageDarkSrc, children, enableImgZoom}) => {
  if (!href) {
    return <div className="card-tile block font-normal group relative my-2 ring-2 ring-transparent rounded-2xl overflow-hidden w-full">
        {imageLightSrc && <img className="w-full m-0 block dark:hidden" src={imageLightSrc} alt={title} {...enableImgZoom ? {
      zoom: true
    } : {
      noZoom: true
    }} />}
        {imageDarkSrc && <img className="w-full m-0 hidden dark:block" src={imageDarkSrc} alt={title} {...enableImgZoom ? {
      zoom: true
    } : {
      noZoom: true
    }} />}
        {title && <h2 className="mt-4 mb-2 px-6 text-base font-semibold">{title}</h2>}
        <div className="px-6 pb-5">{children}</div>
      </div>;
  }
  return <a href={href} className="card-tile block font-normal group relative my-2 ring-2 ring-transparent rounded-2xl overflow-hidden w-full cursor-pointer">
      {imageLightSrc && <img className="w-full m-0 block dark:hidden" src={imageLightSrc} alt={title} noZoom />}
      {imageDarkSrc && <img className="w-full m-0 hidden dark:block" src={imageDarkSrc} alt={title} noZoom />}
      {title && <h2 className="mt-4 mb-2 px-6 text-base font-semibold">{title}</h2>}
      <div className="px-6 pb-5">{children}</div>
    </a>;
};

<Frame className="px-4 py-4" caption="Notification component overview">
  <img className="block dark:hidden" src="https://mintcdn.com/beeq/Z4ffKipt5yZIDQUT/components/images/notification/notification-overview-light.svg?fit=max&auto=format&n=Z4ffKipt5yZIDQUT&q=85&s=ef7bc12be82fd926e65897c00cd65054" alt="BEEQ Notification component overview" width="450" height="407" data-path="components/images/notification/notification-overview-light.svg" />

  <img className="hidden dark:block" src="https://mintcdn.com/beeq/Z4ffKipt5yZIDQUT/components/images/notification/notification-overview-dark.svg?fit=max&auto=format&n=Z4ffKipt5yZIDQUT&q=85&s=fe845e973669d2edd0bed27b03b18021" alt="BEEQ Notification component overview" width="450" height="407" data-path="components/images/notification/notification-overview-dark.svg" />
</Frame>

Notifications inform people about significant events, status changes, or updates that need their awareness without requiring an immediate response. They appear at the edge of the interface, stack gracefully when multiple are present, and dismiss automatically or on demand.

<Note>
  Use notifications for system-level messages and status updates. When feedback belongs directly to a field, control, or focused task area, keep it closer to that context instead.
</Note>

## When to use

<CardGroup cols={2}>
  <Card>
    <span className="flex items-center mr-2 text-lg font-medium" role="heading">
      <Icon className="mr-2" icon="thumbs-up" iconType="solid" size={20} color="var(--bq-stroke--success)" />

      Use notifications when
    </span>

    * Communicating system events, errors, or status changes users need to know about
    * Providing asynchronous feedback that is not tied to a specific field or control
    * Stacking multiple independent alerts in a fixed portal without blocking content
    * Showing updates that users should notice but do not need to answer immediately
  </Card>

  <Card>
    <span className="flex items-center mr-2 text-lg font-medium" role="heading">
      <Icon className="mr-2" icon="thumbs-down" iconType="solid" size={20} color="var(--bq-stroke--danger)" />

      Do not use notifications when
    </span>

    * The feedback is a direct result of a user action and belongs near that action
    * The message is so critical that users must not be able to miss or dismiss it
    * The text is too long to scan quickly
    * The message needs detailed explanation rather than a short, scannable summary
  </Card>
</CardGroup>

## Patterns

<CardGroup cols={2}>
  <CardTile title="Basic">
    A standard notification containing a title and description to give users a quick overview.
  </CardTile>

  <CardTile title="With icon">
    Includes a title, description, and icon so users can distinguish the meaning of the notification more quickly.
  </CardTile>

  <CardTile title="With icon and link">
    Includes a title, icon, and description with an optional link. Links that lead to different pages should open in a new tab.
  </CardTile>

  <CardTile title="With icon, links, and buttons">
    These notifications enable users to take specific actions directly from the notification through footer buttons and linked content.
  </CardTile>
</CardGroup>

## Anatomy

<Frame className="px-4 py-4" caption="Notification component anatomy">
  <img className="block dark:hidden" src="https://mintcdn.com/beeq/Z4ffKipt5yZIDQUT/components/images/notification/notification-anatomy-light.svg?fit=max&auto=format&n=Z4ffKipt5yZIDQUT&q=85&s=a3530adc8371ff8672f6114c91c24e1f" alt="BEEQ Notification component anatomy" width="422" height="256" data-path="components/images/notification/notification-anatomy-light.svg" />

  <img className="hidden dark:block" src="https://mintcdn.com/beeq/Z4ffKipt5yZIDQUT/components/images/notification/notification-anatomy-dark.svg?fit=max&auto=format&n=Z4ffKipt5yZIDQUT&q=85&s=3bc967af47950ede663c5d6238afa9a6" alt="BEEQ Notification component anatomy" width="422" height="256" data-path="components/images/notification/notification-anatomy-dark.svg" />
</Frame>

A notification is composed of a container, an icon, a label, an optional close button, an optional description, and optional call-to-action buttons.

| Part  | Element      | Description                                                                   |
| ----- | ------------ | ----------------------------------------------------------------------------- |
| **1** | Container    | The outer surface that groups the notification content into one message block |
| **2** | Icon         | A semantic icon that communicates the notification type at a glance           |
| **3** | Label        | The primary message in the notification (default slot)                        |
| **4** | Close button | Optional control that allows the user to dismiss the notification manually    |
| **5** | Description  | Optional supporting text or links shown in the `body` slot                    |
| **6** | CTA buttons  | Optional action buttons shown in the `footer` slot                            |

## Design guidelines

Choose the notification treatment that matches the urgency of the message without interrupting the current task.

<CardGroup cols={2}>
  <CardTile title="Keep the message concise" imageLightSrc="/components/images/notification/notification-message-guidance-light.svg" imageDarkSrc="/components/images/notification/notification-message-guidance-dark.svg">
    Lead with the most important information. Notifications work best when users can understand them in a quick scan.
  </CardTile>

  <CardTile title="Use the right semantic type" imageLightSrc="/components/images/notification/notification-type-guidance-light.svg" imageDarkSrc="/components/images/notification/notification-type-guidance-dark.svg">
    Match the type to the meaning of the message so the icon and visual treatment reinforce the content.
  </CardTile>
</CardGroup>

<Steps>
  <Step title="Choose the purpose">
    Start by deciding whether the message is informational, successful, cautionary, or error-related. That determines the right `type`.
  </Step>

  <Step title="Decide whether actions are needed">
    Add footer actions only when users genuinely need a next step such as retrying, reviewing, or navigating.
  </Step>

  <Step title="Choose placement">
    Use inline open notifications for contained layouts. Use `toast()` for stacked, portal-based notifications outside the document flow.
  </Step>
</Steps>

<Note>
  If you use `auto-dismiss`, make sure the timeout gives users enough time to read the content and does not remove important information too quickly.
</Note>

## Usage

### Default

Use the default `info` notification for standard messages and updates.

<CodeLivePreview
  mode="shadow"
  code={`
<style>
:host {
  flex-direction: column !important;
  align-items: stretch !important;
  gap: var(--bq-spacing-m) !important;
}

.footer-btns {
  display: flex;
  gap: var(--bq-spacing-xs);
}

bq-notification[open] {
  display: block;
}
</style>

<bq-notification open>Title</bq-notification>

<bq-notification open>
Title
<span slot="body">
  This is some description text
  <a class="bq-link" href="https://example.com">Link</a>
</span>
</bq-notification>

<bq-notification open>
Title
<span slot="body">
  This is some description text
  <a class="bq-link" href="https://example.com">Link</a>
</span>
<div class="footer-btns" slot="footer">
  <bq-button size="small">Button</bq-button>
  <bq-button appearance="link" size="small">Button</bq-button>
</div>
</bq-notification>

<script>
(() => {
  const notifications = Array.from(previewRoot.querySelectorAll('bq-notification'));
  notifications.forEach((notification) => {
    notification.addEventListener('bqHide', () => {
      setTimeout(() => {
        notification.show();
      }, 1500);
    });
  });
})();
</script>
`}
>
  <CodeGroup>
    ```js JavaScript icon="js" theme={"theme":{"light":"one-light","dark":"night-owl"}}
    const notifications = previewRoot.querySelectorAll("bq-notification");

    notifications.forEach((notification) => {
      notification.addEventListener("bqHide", () => {
        setTimeout(() => notification.show(), 1500);
      });
    });
    ```

    ```html HTML icon="html5" expandable theme={"theme":{"light":"one-light","dark":"night-owl"}}
    <bq-notification open>Title</bq-notification>

    <bq-notification open>
      Title
      <span slot="body">
        This is some description text
        <a class="bq-link" href="https://example.com">Link</a>
      </span>
    </bq-notification>

    <bq-notification open>
      Title
      <span slot="body">
        This is some description text
        <a class="bq-link" href="https://example.com">Link</a>
      </span>
      <div slot="footer">
        <bq-button size="small">Button</bq-button>
        <bq-button appearance="link" size="small">Button</bq-button>
      </div>
    </bq-notification>
    ```

    ```jsx React icon="react" expandable theme={"theme":{"light":"one-light","dark":"night-owl"}}
    import { BqButton, BqNotification } from "@beeq/react";

    const handleHide = (event) => {
      setTimeout(() => event.target.show(), 1500);
    };

    export function DefaultNotification() {
      return (
        <>
          <BqNotification open onBqHide={handleHide}>Title</BqNotification>

          <BqNotification open onBqHide={handleHide}>
            Title
            <span slot="body">
              This is some description text{" "}
              <a className="bq-link" href="https://example.com">Link</a>
            </span>
          </BqNotification>

          <BqNotification open onBqHide={handleHide}>
            Title
            <span slot="body">
              This is some description text{" "}
              <a className="bq-link" href="https://example.com">Link</a>
            </span>
            <div slot="footer">
              <BqButton size="small">Button</BqButton>
              <BqButton appearance="link" size="small">Button</BqButton>
            </div>
          </BqNotification>
        </>
      );
    }
    ```

    ```ts Angular icon="angular" expandable theme={"theme":{"light":"one-light","dark":"night-owl"}}
    import { Component } from "@angular/core";
    import { BqButton, BqNotification } from "@beeq/angular/standalone";

    @Component({
      selector: "app-default-notification",
      standalone: true,
      imports: [BqButton, BqNotification],
      template: `
        <bq-notification open (bqHide)="handleHide($event)">Title</bq-notification>

        <bq-notification open (bqHide)="handleHide($event)">
          Title
          <span slot="body">
            This is some description text
            <a class="bq-link" href="https://example.com">Link</a>
          </span>
        </bq-notification>

        <bq-notification open (bqHide)="handleHide($event)">
          Title
          <span slot="body">
            This is some description text
            <a class="bq-link" href="https://example.com">Link</a>
          </span>
          <div slot="footer">
            <bq-button size="small">Button</bq-button>
            <bq-button appearance="link" size="small">Button</bq-button>
          </div>
        </bq-notification>
      `,
    })
    export class DefaultNotificationComponent {
      handleHide(event: CustomEvent<HTMLBqNotificationElement>): void {
        setTimeout(() => (event.target as HTMLBqNotificationElement).show(), 1500);
      }
    }
    ```

    ```vue Vue icon="vuejs" expandable theme={"theme":{"light":"one-light","dark":"night-owl"}}
    <script setup lang="ts">
    import { BqButton, BqNotification } from "@beeq/vue";

    const handleHide = (event: CustomEvent<HTMLBqNotificationElement>) => {
      setTimeout(() => (event.target as HTMLBqNotificationElement).show(), 1500);
    };
    </script>

    <template>
      <BqNotification open @bqHide="handleHide">Title</BqNotification>

      <BqNotification open @bqHide="handleHide">
        Title
        <span slot="body">
          This is some description text
          <a class="bq-link" href="https://example.com">Link</a>
        </span>
      </BqNotification>

      <BqNotification open @bqHide="handleHide">
        Title
        <span slot="body">
          This is some description text
          <a class="bq-link" href="https://example.com">Link</a>
        </span>
        <div slot="footer">
          <BqButton size="small">Button</BqButton>
          <BqButton appearance="link" size="small">Button</BqButton>
        </div>
      </BqNotification>
    </template>
    ```
  </CodeGroup>
</CodeLivePreview>

<Tip>
  Notifications take up the full width of their parent container. Wrap them in a container with a `max-width` if you need a more constrained layout.
</Tip>

## Options

### Semantic types

Use semantic `type` values to communicate severity and intent clearly.

<CodeLivePreview
  mode="shadow"
  code={`
<style>
:host {
  flex-direction: column !important;
  align-items: stretch !important;
  gap: var(--bq-spacing-m) !important;
}

bq-notification[open] {
  display: block;
}
</style>

<bq-notification open type="error">Error</bq-notification>
<bq-notification open type="neutral">Neutral</bq-notification>
<bq-notification open type="success">Success</bq-notification>
<bq-notification open type="warning">Warning</bq-notification>

<script>
(() => {
  const notifications = Array.from(previewRoot.querySelectorAll('bq-notification'));
  notifications.forEach((notification) => {
    notification.addEventListener('bqHide', () => {
      setTimeout(() => {
        notification.show();
      }, 1500);
    });
  });
})();
</script>
`}
>
  <CodeGroup>
    ```js JavaScript icon="js" theme={"theme":{"light":"one-light","dark":"night-owl"}}
    const notifications = previewRoot.querySelectorAll("bq-notification");

    notifications.forEach((notification) => {
      notification.addEventListener("bqHide", () => {
        setTimeout(() => notification.show(), 1500);
      });
    });
    ```

    ```html HTML icon="html5" theme={"theme":{"light":"one-light","dark":"night-owl"}}
    <bq-notification open type="error">Error</bq-notification>
    <bq-notification open type="neutral">Neutral</bq-notification>
    <bq-notification open type="success">Success</bq-notification>
    <bq-notification open type="warning">Warning</bq-notification>
    ```

    ```jsx React icon="react" expandable theme={"theme":{"light":"one-light","dark":"night-owl"}}
    import { BqNotification } from "@beeq/react";

    const handleHide = (event) => {
      setTimeout(() => event.target.show(), 1500);
    };

    export function SemanticTypesNotification() {
      return (
        <>
          <BqNotification open type="error" onBqHide={handleHide}>Error</BqNotification>
          <BqNotification open type="neutral" onBqHide={handleHide}>Neutral</BqNotification>
          <BqNotification open type="success" onBqHide={handleHide}>Success</BqNotification>
          <BqNotification open type="warning" onBqHide={handleHide}>Warning</BqNotification>
        </>
      );
    }
    ```

    ```ts Angular icon="angular" expandable theme={"theme":{"light":"one-light","dark":"night-owl"}}
    import { Component } from "@angular/core";
    import { BqNotification } from "@beeq/angular/standalone";

    @Component({
      selector: "app-semantic-types-notification",
      standalone: true,
      imports: [BqNotification],
      template: `
        <bq-notification open type="error" (bqHide)="handleHide($event)">Error</bq-notification>
        <bq-notification open type="neutral" (bqHide)="handleHide($event)">Neutral</bq-notification>
        <bq-notification open type="success" (bqHide)="handleHide($event)">Success</bq-notification>
        <bq-notification open type="warning" (bqHide)="handleHide($event)">Warning</bq-notification>
      `,
    })
    export class SemanticTypesNotificationComponent {
      handleHide(event: CustomEvent<HTMLBqNotificationElement>): void {
        setTimeout(() => (event.target as HTMLBqNotificationElement).show(), 1500);
      }
    }
    ```

    ```vue Vue icon="vuejs" expandable theme={"theme":{"light":"one-light","dark":"night-owl"}}
    <script setup lang="ts">
    import { BqNotification } from "@beeq/vue";

    const handleHide = (event: CustomEvent<HTMLBqNotificationElement>) => {
      setTimeout(() => (event.target as HTMLBqNotificationElement).show(), 1500);
    };
    </script>

    <template>
      <BqNotification open type="error" @bqHide="handleHide">Error</BqNotification>
      <BqNotification open type="neutral" @bqHide="handleHide">Neutral</BqNotification>
      <BqNotification open type="success" @bqHide="handleHide">Success</BqNotification>
      <BqNotification open type="warning" @bqHide="handleHide">Warning</BqNotification>
    </template>
    ```
  </CodeGroup>
</CodeLivePreview>

### Custom icon

Use the `icon` slot to replace the default type icon with a custom `bq-icon`.

<CodeLivePreview
  mode="shadow"
  code={`
<style>
:host {
  flex-direction: column !important;
  align-items: stretch !important;
  gap: var(--bq-spacing-m) !important;
}

.footer-btns {
  display: flex;
  gap: var(--bq-spacing-xs);
}

bq-notification[open] {
  display: block;
}
</style>

<bq-notification open type="info">
<bq-icon name="thumbs-up" slot="icon"></bq-icon>
Title
</bq-notification>

<bq-notification open type="info">
<bq-icon name="thumbs-up" slot="icon"></bq-icon>
Title
<span slot="body">
  This is some description text
  <a class="bq-link" href="https://example.com">Link</a>
</span>
</bq-notification>

<bq-notification open type="info">
<bq-icon name="thumbs-up" slot="icon"></bq-icon>
Title
<span slot="body">
  This is some description text
  <a class="bq-link" href="https://example.com">Link</a>
</span>
<div class="footer-btns" slot="footer">
  <bq-button size="small">Button</bq-button>
  <bq-button appearance="link" size="small">Button</bq-button>
</div>
</bq-notification>

<script>
(() => {
  const notifications = Array.from(previewRoot.querySelectorAll('bq-notification'));
  notifications.forEach((notification) => {
    notification.addEventListener('bqHide', () => {
      setTimeout(() => {
        notification.show();
      }, 1500);
    });
  });
})();
</script>
`}
>
  <CodeGroup>
    ```js JavaScript icon="js" theme={"theme":{"light":"one-light","dark":"night-owl"}}
    const notifications = previewRoot.querySelectorAll("bq-notification");

    notifications.forEach((notification) => {
      notification.addEventListener("bqHide", () => {
        setTimeout(() => notification.show(), 1500);
      });
    });
    ```

    ```html HTML icon="html5" expandable theme={"theme":{"light":"one-light","dark":"night-owl"}}
    <bq-notification open type="info">
      <bq-icon name="thumbs-up" slot="icon"></bq-icon>
      Title
    </bq-notification>

    <bq-notification open type="info">
      <bq-icon name="thumbs-up" slot="icon"></bq-icon>
      Title
      <span slot="body">
        This is some description text
        <a class="bq-link" href="https://example.com">Link</a>
      </span>
    </bq-notification>

    <bq-notification open type="info">
      <bq-icon name="thumbs-up" slot="icon"></bq-icon>
      Title
      <span slot="body">
        This is some description text
        <a class="bq-link" href="https://example.com">Link</a>
      </span>
      <div slot="footer">
        <bq-button size="small">Button</bq-button>
        <bq-button appearance="link" size="small">Button</bq-button>
      </div>
    </bq-notification>
    ```

    ```jsx React icon="react" expandable theme={"theme":{"light":"one-light","dark":"night-owl"}}
    import { BqButton, BqIcon, BqNotification } from "@beeq/react";

    const handleHide = (event) => {
      setTimeout(() => event.target.show(), 1500);
    };

    export function CustomIconNotification() {
      return (
        <>
          <BqNotification open type="info" onBqHide={handleHide}>
            <BqIcon name="thumbs-up" slot="icon" />
            Title
          </BqNotification>

          <BqNotification open type="info" onBqHide={handleHide}>
            <BqIcon name="thumbs-up" slot="icon" />
            Title
            <span slot="body">
              This is some description text{" "}
              <a className="bq-link" href="https://example.com">Link</a>
            </span>
          </BqNotification>

          <BqNotification open type="info" onBqHide={handleHide}>
            <BqIcon name="thumbs-up" slot="icon" />
            Title
            <span slot="body">
              This is some description text{" "}
              <a className="bq-link" href="https://example.com">Link</a>
            </span>
            <div slot="footer">
              <BqButton size="small">Button</BqButton>
              <BqButton appearance="link" size="small">Button</BqButton>
            </div>
          </BqNotification>
        </>
      );
    }
    ```

    ```ts Angular icon="angular" expandable theme={"theme":{"light":"one-light","dark":"night-owl"}}
    import { Component } from "@angular/core";
    import { BqButton, BqIcon, BqNotification } from "@beeq/angular/standalone";

    @Component({
      selector: "app-custom-icon-notification",
      standalone: true,
      imports: [BqButton, BqIcon, BqNotification],
      template: `
        <bq-notification open type="info" (bqHide)="handleHide($event)">
          <bq-icon name="thumbs-up" slot="icon"></bq-icon>
          Title
        </bq-notification>

        <bq-notification open type="info" (bqHide)="handleHide($event)">
          <bq-icon name="thumbs-up" slot="icon"></bq-icon>
          Title
          <span slot="body">
            This is some description text
            <a class="bq-link" href="https://example.com">Link</a>
          </span>
        </bq-notification>

        <bq-notification open type="info" (bqHide)="handleHide($event)">
          <bq-icon name="thumbs-up" slot="icon"></bq-icon>
          Title
          <span slot="body">
            This is some description text
            <a class="bq-link" href="https://example.com">Link</a>
          </span>
          <div slot="footer">
            <bq-button size="small">Button</bq-button>
            <bq-button appearance="link" size="small">Button</bq-button>
          </div>
        </bq-notification>
      `,
    })
    export class CustomIconNotificationComponent {
      handleHide(event: CustomEvent<HTMLBqNotificationElement>): void {
        setTimeout(() => (event.target as HTMLBqNotificationElement).show(), 1500);
      }
    }
    ```

    ```vue Vue icon="vuejs" expandable theme={"theme":{"light":"one-light","dark":"night-owl"}}
    <script setup lang="ts">
    import { BqButton, BqIcon, BqNotification } from "@beeq/vue";

    const handleHide = (event: CustomEvent<HTMLBqNotificationElement>) => {
      setTimeout(() => (event.target as HTMLBqNotificationElement).show(), 1500);
    };
    </script>

    <template>
      <BqNotification open type="info" @bqHide="handleHide">
        <BqIcon name="thumbs-up" slot="icon" />
        Title
      </BqNotification>

      <BqNotification open type="info" @bqHide="handleHide">
        <BqIcon name="thumbs-up" slot="icon" />
        Title
        <span slot="body">
          This is some description text
          <a class="bq-link" href="https://example.com">Link</a>
        </span>
      </BqNotification>

      <BqNotification open type="info" @bqHide="handleHide">
        <BqIcon name="thumbs-up" slot="icon" />
        Title
        <span slot="body">
          This is some description text
          <a class="bq-link" href="https://example.com">Link</a>
        </span>
        <div slot="footer">
          <BqButton size="small">Button</BqButton>
          <BqButton appearance="link" size="small">Button</BqButton>
        </div>
      </BqNotification>
    </template>
    ```
  </CodeGroup>
</CodeLivePreview>

### Stacked notifications

Use the `toast()` method to render notifications in a fixed-position portal that stacks multiple notifications vertically outside the normal document flow. The component creates and reuses a `.bq-notification-portal` element attached to `document.body`, and BEEQ global styles position that portal for you.

In framework apps, prefer a framework-owned portal or stack when you want the notification lifecycle to stay declarative.

<CodeLivePreview
  mode="shadow"
  code={`
<style>
:host {
  align-items: flex-start !important;
  flex-direction: column !important;
  gap: var(--bq-spacing-s) !important;
}
</style>

<p>The notification portal stacks multiple notifications vertically in a fixed position.</p>
<p>Only one portal exists at a time and it is added or removed from the DOM as needed.</p>
<bq-button id="notification-stacked-btn">Open notification</bq-button>

<script>
(() => {
  const NOTIFICATION_TYPE = ['error', 'info', 'neutral', 'success', 'warning'];
  const getRandomType = () => NOTIFICATION_TYPE[Math.floor(Math.random() * NOTIFICATION_TYPE.length)];

  const openBtn = previewRoot.querySelector('#notification-stacked-btn');
  if (!openBtn) return;

  const handleOpen = async () => {
    await customElements.whenDefined('bq-notification');

    const type = getRandomType();
    const notification = Object.assign(document.createElement('bq-notification'), {
      type,
      autoDismiss: true,
      innerHTML: \`
        Title
        <span slot="body">
          Here goes the description for the <strong>\${type} notification</strong><br />
          You can also add a <a class="bq-link" href="https://example.com">Link</a>
        </span>
      \`,
    });

    document.body.append(notification);
    notification.toast();
  };

  openBtn.addEventListener('bqClick', handleOpen);
})();
</script>
`}
>
  <CodeGroup>
    ```js JavaScript icon="js" expandable theme={"theme":{"light":"one-light","dark":"night-owl"}}
    const openBtn = previewRoot.querySelector("#notification-stacked-btn");

    const NOTIFICATION_TYPES = ["error", "info", "neutral", "success", "warning"];
    const getRandomType = () => NOTIFICATION_TYPES[Math.floor(Math.random() * NOTIFICATION_TYPES.length)];

    openBtn.addEventListener("bqClick", async () => {
      await customElements.whenDefined("bq-notification");

      const type = getRandomType();
      const notification = Object.assign(document.createElement("bq-notification"), {
        type,
        autoDismiss: true,
        innerHTML: `
          Title
          <span slot="body">
            Here goes the description for the <strong>${type} notification</strong><br />
            You can also add a <a class="bq-link" href="https://example.com">Link</a>
          </span>
        `,
      });

      document.body.append(notification);
      notification.toast();
    });
    ```

    ```html HTML icon="html5" theme={"theme":{"light":"one-light","dark":"night-owl"}}
    <p>The notification portal stacks multiple notifications vertically in a fixed position.</p>
    <p>Only one portal exists at a time and it is added or removed from the DOM as needed.</p>
    <bq-button id="notification-stacked-btn">Open notification</bq-button>
    ```

    ```tsx React icon="react" expandable theme={"theme":{"light":"one-light","dark":"night-owl"}}
    import type { CSSProperties } from "react";
    import { useCallback, useRef, useState } from "react";
    import { createPortal } from "react-dom";
    import { BqButton, BqNotification } from "@beeq/react";

    const NOTIFICATION_TYPES = ["error", "info", "neutral", "success", "warning"] as const;
    type NotificationType = (typeof NOTIFICATION_TYPES)[number];
    type NotificationItem = { id: number; type: NotificationType };

    const notificationPortalStyles: CSSProperties = {
      position: "fixed",
      insetInlineEnd: 0,
      top: 0,
      zIndex: 50,
      maxBlockSize: "100%",
      maxInlineSize: "100%",
    };

    const notificationStyles: CSSProperties = {
      display: "block",
      margin: "var(--bq-spacing-m)",
    };

    const getRandomType = () => NOTIFICATION_TYPES[Math.floor(Math.random() * NOTIFICATION_TYPES.length)];

    export function StackedNotifications() {
      const [notifications, setNotifications] = useState<NotificationItem[]>([]);
      const nextNotificationId = useRef(0);
      const portalTarget = typeof document === "undefined" ? null : document.body;

      const handleOpen = useCallback(() => {
        setNotifications((current) => [
          ...current,
          { id: ++nextNotificationId.current, type: getRandomType() },
        ]);
      }, []);

      const handleAfterClose = useCallback((id: number) => {
        setNotifications((current) => current.filter((notification) => notification.id !== id));
      }, []);

      return (
        <>
          <p>The notification portal stacks multiple notifications vertically in a fixed position.</p>
          <p>Only one portal exists at a time and it is added or removed from the DOM as needed.</p>
          <BqButton onBqClick={handleOpen}>Open notification</BqButton>

          {portalTarget &&
            createPortal(
              <div aria-label="Notifications" style={notificationPortalStyles}>
                {notifications.map(({ id, type }) => (
                  <BqNotification
                    key={id}
                    open
                    autoDismiss
                    type={type}
                    onBqAfterClose={() => handleAfterClose(id)}
                    style={notificationStyles}
                  >
                    Title
                    <span slot="body">
                      Here goes the description for the <strong>{type} notification</strong>
                      <br />
                      You can also add a <a className="bq-link" href="https://example.com">Link</a>
                    </span>
                  </BqNotification>
                ))}
              </div>,
              portalTarget,
            )}
        </>
      );
    }
    ```

    ```ts Angular icon="angular" expandable theme={"theme":{"light":"one-light","dark":"night-owl"}}
    import { Component } from "@angular/core";
    import { BqButton, BqNotification } from "@beeq/angular/standalone";

    type NotificationType = "error" | "info" | "neutral" | "success" | "warning";
    interface NotificationItem { id: number; type: NotificationType; }

    const NOTIFICATION_TYPES: NotificationType[] = ["error", "info", "neutral", "success", "warning"];

    @Component({
      selector: "app-stacked-notifications",
      standalone: true,
      imports: [BqButton, BqNotification],
      template: `
        <p>The notification portal stacks multiple notifications vertically in a fixed position.</p>
        <p>Only one portal exists at a time and it is added or removed from the DOM as needed.</p>
        <bq-button (bqClick)="openNotification()">Open notification</bq-button>

        <div class="notification-portal" aria-label="Notifications">
          @for (notification of notifications; track notification.id) {
            <bq-notification
              open
              auto-dismiss
              [type]="notification.type"
              (bqAfterClose)="removeNotification(notification.id)"
            >
              Title
              <span slot="body">
                Here goes the description for the <strong>{{ notification.type }} notification</strong><br />
                You can also add a <a class="bq-link" href="https://example.com">Link</a>
              </span>
            </bq-notification>
          }
        </div>
      `,
      styles: [`
        .notification-portal {
          position: fixed;
          inset-inline-end: 0;
          top: 0;
          z-index: 50;
          max-block-size: 100%;
          max-inline-size: 100%;
        }

        .notification-portal bq-notification {
          display: block;
          margin: var(--bq-spacing-m);
        }
      `],
    })
    export class StackedNotificationsComponent {
      notifications: NotificationItem[] = [];
      private nextNotificationId = 0;

      openNotification(): void {
        this.notifications = [
          ...this.notifications,
          { id: ++this.nextNotificationId, type: this.getRandomType() },
        ];
      }

      removeNotification(id: number): void {
        this.notifications = this.notifications.filter((notification) => notification.id !== id);
      }

      private getRandomType(): NotificationType {
        return NOTIFICATION_TYPES[Math.floor(Math.random() * NOTIFICATION_TYPES.length)];
      }
    }
    ```

    ```vue Vue icon="vuejs" expandable theme={"theme":{"light":"one-light","dark":"night-owl"}}
    <script setup lang="ts">
    import { ref } from "vue";
    import { BqButton, BqNotification } from "@beeq/vue";

    const NOTIFICATION_TYPES = ["error", "info", "neutral", "success", "warning"] as const;
    type NotificationType = (typeof NOTIFICATION_TYPES)[number];
    interface NotificationItem { id: number; type: NotificationType; }

    const notifications = ref<NotificationItem[]>([]);
    let nextNotificationId = 0;
    const getRandomType = () => NOTIFICATION_TYPES[Math.floor(Math.random() * NOTIFICATION_TYPES.length)];

    const openNotification = () => {
      notifications.value.push({ id: ++nextNotificationId, type: getRandomType() });
    };

    const removeNotification = (id: number) => {
      notifications.value = notifications.value.filter((notification) => notification.id !== id);
    };
    </script>

    <template>
      <p>The notification portal stacks multiple notifications vertically in a fixed position.</p>
      <p>Only one portal exists at a time and it is added or removed from the DOM as needed.</p>
      <BqButton @bqClick="openNotification">Open notification</BqButton>

      <Teleport to="body">
        <div class="notification-portal" aria-label="Notifications">
          <BqNotification
            v-for="notification in notifications"
            :key="notification.id"
            open
            autoDismiss
            :type="notification.type"
            @bqAfterClose="removeNotification(notification.id)"
          >
            Title
            <span slot="body">
              Here goes the description for the <strong>{{ notification.type }} notification</strong><br />
              You can also add a <a class="bq-link" href="https://example.com">Link</a>
            </span>
          </BqNotification>
        </div>
      </Teleport>
    </template>

    <style>
    .notification-portal {
      position: fixed;
      inset-inline-end: 0;
      top: 0;
      z-index: 50;
      max-block-size: 100%;
      max-inline-size: 100%;
    }

    .notification-portal bq-notification {
      display: block;
      margin: var(--bq-spacing-m);
    }
    </style>
    ```
  </CodeGroup>
</CodeLivePreview>

## Best practices

<CardGroup cols={2}>
  <Card>
    <span className="flex items-center mr-2 text-lg font-medium" role="heading">
      <Icon className="mr-2" icon="check" iconType="solid" size={20} color="var(--bq-stroke--success)" />

      Do
    </span>

    Position notifications at the edges of the screen so they do not block the main content.
  </Card>

  <Card>
    <span className="flex items-center mr-2 text-lg font-medium" role="heading">
      <Icon className="mr-2" icon="xmark" iconType="solid" size={20} color="var(--bq-stroke--danger)" />

      Don't
    </span>

    Do not position notifications in the center of the screen where they compete with the main task.
  </Card>

  <Card>
    <span className="flex items-center mr-2 text-lg font-medium" role="heading">
      <Icon className="mr-2" icon="check" iconType="solid" size={20} color="var(--bq-stroke--success)" />

      Do
    </span>

    Keep messages concise and direct, and lead with the most important information.
  </Card>

  <Card>
    <span className="flex items-center mr-2 text-lg font-medium" role="heading">
      <Icon className="mr-2" icon="xmark" iconType="solid" size={20} color="var(--bq-stroke--danger)" />

      Don't
    </span>

    Do not use long descriptions in notifications because they reduce scanability.
  </Card>

  <Card>
    <span className="flex items-center mr-2 text-lg font-medium" role="heading">
      <Icon className="mr-2" icon="check" iconType="solid" size={20} color="var(--bq-stroke--success)" />

      Do
    </span>

    Use the correct `type` so the icon and visual treatment reinforce the message severity.
  </Card>

  <Card>
    <span className="flex items-center mr-2 text-lg font-medium" role="heading">
      <Icon className="mr-2" icon="xmark" iconType="solid" size={20} color="var(--bq-stroke--danger)" />

      Don't
    </span>

    Do not rely on custom colors or ambiguous language to communicate urgency.
  </Card>

  <Card>
    <span className="flex items-center mr-2 text-lg font-medium" role="heading">
      <Icon className="mr-2" icon="check" iconType="solid" size={20} color="var(--bq-stroke--success)" />

      Do
    </span>

    Provide a close button or a thoughtful auto-dismiss timeout so users can dismiss notifications predictably.
  </Card>

  <Card>
    <span className="flex items-center mr-2 text-lg font-medium" role="heading">
      <Icon className="mr-2" icon="xmark" iconType="solid" size={20} color="var(--bq-stroke--danger)" />

      Don't
    </span>

    Do not use all-caps or unnecessary typographic emphasis in notification copy.
  </Card>
</CardGroup>

## Accessibility

* **Built-in alert role** — the component applies `role="alert"` to the notification host element, which implicitly carries `aria-live="assertive"`. Assistive technology announces new notifications immediately when they appear.
* **Visibility tied to `open`** — the host also receives `aria-hidden="true"` when `open` is `false`, removing it from the accessibility tree when not visible.
* **Color alone is not enough**: the icon and message text should work together so users who cannot perceive color still understand the notification.
* **Keep interactive elements reachable**: users must be able to tab to the close button, links, and footer actions and activate them with the keyboard.
* **Use considerate auto-dismiss timing**: if `auto-dismiss` is enabled, give users enough time to read the content before it disappears.
* **Do not steal focus**: stacked notifications are non-modal and should not move focus unexpectedly away from the current task.

## API reference

### Properties

| Property       | Attribute       | Description                                                                              | Type                                                       | Default     |
| -------------- | --------------- | ---------------------------------------------------------------------------------------- | ---------------------------------------------------------- | ----------- |
| `autoDismiss`  | `auto-dismiss`  | If `true`, the notification closes automatically after `time` milliseconds               | `boolean`                                                  | `undefined` |
| `border`       | `border`        | Border radius of the notification component                                              | `"none" \| "xs2" \| "xs" \| "s" \| "m" \| "l" \| "full"`   | `"s"`       |
| `disableClose` | `disable-close` | If `true`, the close button is not shown                                                 | `boolean`                                                  | `undefined` |
| `hideIcon`     | `hide-icon`     | If `true`, the type icon is not shown                                                    | `boolean`                                                  | `undefined` |
| `open`         | `open`          | If `true`, the notification is visible                                                   | `boolean`                                                  | `undefined` |
| `time`         | `time`          | Duration in milliseconds before auto-dismissal. Only valid when `auto-dismiss` is `true` | `number`                                                   | `3000`      |
| `type`         | `type`          | The semantic type of the notification                                                    | `"error" \| "info" \| "neutral" \| "success" \| "warning"` | `"info"`    |

### Events

| Event          | Description                                   | Type                                     |
| -------------- | --------------------------------------------- | ---------------------------------------- |
| `bqAfterClose` | Fired after the notification finishes closing | `CustomEvent<void>`                      |
| `bqAfterOpen`  | Fired after the notification finishes opening | `CustomEvent<void>`                      |
| `bqHide`       | Fired when the notification is about to hide  | `CustomEvent<HTMLBqNotificationElement>` |
| `bqShow`       | Fired when the notification is about to show  | `CustomEvent<HTMLBqNotificationElement>` |

<Note>
  * In React, prefix events with `on`: `onBqHide`, `onBqShow`, `onBqAfterClose`, `onBqAfterOpen`.
  * In Angular, use the event binding syntax: `(bqHide)`, `(bqShow)`, `(bqAfterClose)`, `(bqAfterOpen)`.
  * In Vue, use the `@` shorthand: `@bqHide`, `@bqShow`, `@bqAfterClose`, `@bqAfterOpen`.
</Note>

### Methods

| Method    | Description                                                    | Returns         |
| --------- | -------------------------------------------------------------- | --------------- |
| `hide()`  | Hides the notification                                         | `Promise<void>` |
| `show()`  | Shows the notification                                         | `Promise<void>` |
| `toast()` | Renders the notification in the fixed-position stacking portal | `Promise<void>` |

### Slots

| Slot        | Description                                            |
| ----------- | ------------------------------------------------------ |
| *(default)* | The notification title content                         |
| `body`      | The notification description content                   |
| `btn-close` | Replaces the default close button content              |
| `footer`    | The notification footer content                        |
| `icon`      | Replaces the default type icon with a custom `bq-icon` |

### Shadow parts

| Part           | Description                                                     |
| -------------- | --------------------------------------------------------------- |
| `base`         | The `<div>` container of the predefined `bq-icon`               |
| `body`         | The `<div>` wrapping the notification description content       |
| `btn-close`    | The `bq-button` used to close the notification                  |
| `content`      | The `<div>` wrapping all notification content                   |
| `footer`       | The `<div>` wrapping the notification footer content            |
| `icon`         | The `<bq-icon>` element rendered based on the notification type |
| `icon-outline` | The `<div>` wrapping the icon element                           |
| `main`         | The `<div>` wrapping the notification main content              |
| `svg`          | The `<svg>` element of the predefined `bq-icon`                 |
| `title`        | The `<div>` wrapping the notification title content             |
| `wrapper`      | The outermost wrapper `<div>` inside the shadow DOM             |

### CSS custom properties

<Expandable title="CSS variables" defaultOpen={false}>
  | Variable                                | Description                          | Default                   |
  | --------------------------------------- | ------------------------------------ | ------------------------- |
  | `--bq-notification--background`         | Notification background color        | `var(--bq-ui--primary)`   |
  | `--bq-notification--border-color`       | Notification border color            | `transparent`             |
  | `--bq-notification--border-radius`      | Notification border radius           | `var(--bq-radius--s)`     |
  | `--bq-notification--border-style`       | Notification border style            | `none`                    |
  | `--bq-notification--border-width`       | Notification border width            | `unset`                   |
  | `--bq-notification--box-shadow`         | Notification box shadow              | `var(--bq-box-shadow--l)` |
  | `--bq-notification--content-footer-gap` | Gap between content and footer       | `var(--bq-spacing-m)`     |
  | `--bq-notification--icon-color-error`   | Icon color for error notifications   | `var(--bq-icon--danger)`  |
  | `--bq-notification--icon-color-info`    | Icon color for info notifications    | `var(--bq-icon--brand)`   |
  | `--bq-notification--icon-color-neutral` | Icon color for neutral notifications | `var(--bq-icon--primary)` |
  | `--bq-notification--icon-color-success` | Icon color for success notifications | `var(--bq-icon--success)` |
  | `--bq-notification--icon-color-warning` | Icon color for warning notifications | `var(--bq-icon--warning)` |
  | `--bq-notification--min-width`          | Minimum notification width           | `320px`                   |
  | `--bq-notification--padding`            | Notification padding                 | `var(--bq-spacing-m)`     |
  | `--bq-notification--title-body-gap`     | Gap between title and body           | `var(--bq-spacing-s)`     |
</Expandable>

<Tip>
  Learn more about [styling with shadow parts](/guides/styles#component-shadow-dom-parts) and [CSS custom properties](/guides/styles#global-css-custom-properties).
</Tip>

## Resources

<CardGroup cols={2}>
  <Card horizontal title="Interactive playground" icon="code" href="https://storybook.beeq.design/?path=/story/components-notification--default">
    Explore all notification types and states in the Storybook playground.
  </Card>

  <Card horizontal title="Source code" icon="github" href="https://github.com/Endava/BEEQ/tree/main/packages/beeq/src/components/notification">
    Browse the full source code, including implementation, tests, and styles.
  </Card>
</CardGroup>
