> Markdown version of [Notification](https://vaadin.com/docs/v24/components/notification). Section index: [llms.txt](https://vaadin.com/docs/v24/components/llms.txt)

# Notification

Notification is used to provide feedback to the user about activities, processes, and events in the application.

**Lit** — `notification-basic.ts`

```typescript
const notification = Notification.show('Financial report generated', {
  position: 'middle',
  duration: 0,
});
```

**Flow** — `NotificationBasic.java`

```java
// When creating a notification using the `show` static method,
// the duration is 5-sec by default.
Notification notification = Notification
        .show("Financial report generated");
```

**React** — `notification-basic.tsx`

```typescript
const notification = Notification.show('Financial report generated', {
  position: 'middle',
  duration: 0,
});
```

## <a id="theme-variants"></a>Theme Variants

Notification comes with a few theme variants: [`success`](#success); [`warning`](#warning); [`error`](#error); [`primary`](#primary); and [`contrast`](#contrast). These variants are described in the following sub-sections:

### <a id="success"></a>Success

The `success` theme variant can be used to display success messages, such as when a task or operation is completed.

**Lit** — `notification-success.ts`

```typescript
const notification = Notification.show('Application submitted!', {
  position: 'middle',
  duration: 0,
  theme: 'success',
});
```

**Flow** — `NotificationSuccess.java`

```java
Notification notification = Notification.show("Application submitted!");
notification.addThemeVariants(NotificationVariant.LUMO_SUCCESS);
```

**React** — `notification-success.tsx`

```tsx
const notification = Notification.show('Financial report generated', {
  position: 'middle',
  duration: 0,
  theme: 'success',
});
```

Users shouldn’t be notified always, or even frequently, of successful operations. Too many notifications can be more distracting than helpful to users. Use success notifications only for operations whose successful completion may otherwise be difficult to discern.

### <a id="warning"></a>Warning (since V24.1)

The `warning` theme variant can be used to display warnings.

**Lit** — `notification-warning.ts`

```html
<!-- The duration is set to 0-sec to prevent the notification from auto-close. -->
<vaadin-notification
  theme="warning"
  duration="0"
  position="middle"
  .opened="${this.notificationOpened}"
  @closed="${() => {
    this.notificationOpened = false;
  }}"
  ${notificationRenderer(this.renderer, [])}
></vaadin-notification>

<!-- ... -->

renderer: NotificationLitRenderer = () => html`
  <vaadin-horizontal-layout theme="spacing" style="align-items: center;">
    <div>
      Your session will expire in 5 minutes due to inactivity.<br />
      Close this warning to continue working.
    </div>
    <vaadin-button theme="tertiary-inline" @click="${this.close}" aria-label="Close">
      <vaadin-icon icon="lumo:cross"></vaadin-icon>
    </vaadin-button>
  </vaadin-horizontal-layout>
`;
```

**Flow** — `NotificationWarning.java`

```java
// When creating a notification using the constructor,
// the duration is 0-sec by default which means that
// the notification does not close automatically.
Notification notification = new Notification();
notification.addThemeVariants(NotificationVariant.LUMO_WARNING);

Div text = new Div(new Text(
        "Your session will expire in 5 minutes due to inactivity."),
        new HtmlComponent("br"),
        new Text("Close this warning to continue working."));

Button closeButton = new Button(new Icon("lumo", "cross"));
closeButton.addThemeVariants(ButtonVariant.LUMO_TERTIARY_INLINE);
closeButton.setAriaLabel("Close");
closeButton.addClickListener(event -> {
    notification.close();
});

HorizontalLayout layout = new HorizontalLayout(text, closeButton);
layout.setAlignItems(Alignment.CENTER);

notification.add(layout);
notification.open();
```

**React** — `notification-warning.tsx`

```tsx
{/* The duration is set to 0-sec to prevent the notification from auto-close. */}
<Notification
  theme="warning"
  duration={0}
  position="middle"
  opened={notificationOpened.value}
  onClosed={() => {
    notificationOpened.value = false;
  }}
>
  <HorizontalLayout theme="spacing" style={{ alignItems: 'center' }}>
    <div>
      Your session will expire in 5 minutes due to inactivity.
      <br />
      Close this warning to continue working.
    </div>

    <Button
      theme="tertiary-inline"
      onClick={() => {
        notificationOpened.value = false;
      }}
    >
      <Icon icon="lumo:cross" />
    </Button>
  </HorizontalLayout>
</Notification>
```

Warning notifications should be persistent, and provide the user with a button that closes the notification or allows the user to take appropriate action.

### <a id="error"></a>Error

The `error` theme variant can be used to display errors.

**Lit** — `notification-error.ts`

```html
<!-- The duration is set to 0-sec to prevent the notification from auto-close. -->
<vaadin-notification
  theme="error"
  duration="0"
  position="middle"
  .opened="${this.notificationOpened}"
  @closed="${() => {
    this.notificationOpened = false;
  }}"
  ${notificationRenderer(this.renderer, [])}
></vaadin-notification>

<!-- ... -->

renderer: NotificationLitRenderer = () => html`
  <vaadin-horizontal-layout theme="spacing" style="align-items: center;">
    <div>Failed to generate report</div>
    <vaadin-button theme="tertiary-inline" @click="${this.close}" aria-label="Close">
      <vaadin-icon icon="lumo:cross"></vaadin-icon>
    </vaadin-button>
  </vaadin-horizontal-layout>
`;
```

**Flow** — `NotificationError.java`

```java
// When creating a notification using the constructor,
// the duration is 0-sec by default which means that
// the notification does not close automatically.
Notification notification = new Notification();
notification.addThemeVariants(NotificationVariant.LUMO_ERROR);

Div text = new Div(new Text("Failed to generate report"));

Button closeButton = new Button(new Icon("lumo", "cross"));
closeButton.addThemeVariants(ButtonVariant.LUMO_TERTIARY_INLINE);
closeButton.setAriaLabel("Close");
closeButton.addClickListener(event -> {
    notification.close();
});

HorizontalLayout layout = new HorizontalLayout(text, closeButton);
layout.setAlignItems(Alignment.CENTER);

notification.add(layout);
notification.open();
```

**React** — `notification-error.tsx`

```tsx
{/* The duration is set to 0-sec to prevent the notification from auto-close. */}
<Notification
  theme="error"
  duration={0}
  position="middle"
  opened={notificationOpened.value}
  onClosed={() => {
    notificationOpened.value = false;
  }}
>
  <HorizontalLayout theme="spacing" style={{ alignItems: 'center' }}>
    <div>Failed to generate report</div>
    <Button
      theme="tertiary-inline"
      onClick={() => {
        notificationOpened.value = false;
      }}
      aria-label="Close"
    >
      <Icon icon="lumo:cross" />
    </Button>
  </HorizontalLayout>
</Notification>
```

Error notifications should be persistent, and provide the user with a button that closes the notification or allows the user to take appropriate action.

Notifications are non-modal and can be ignored. Therefore, they’re usually inappropriate for displaying unexpected technical errors that prevent the application from functioning, or situations that require immediate user action. Use instead a modal [Dialog](https://vaadin.com/docs/v24/components/dialog.md) in such situations.

The built-in error message feature, included with input field components, should be used for field-specific input validation errors.

### <a id="primary"></a>Primary

The `primary` theme variant can be used for important informational messages or to draw extra attention to a notification.

**Lit** — `notification-primary.ts`

```typescript
const notification = Notification.show('New project plan available', {
  position: 'middle',
  duration: 0,
  theme: 'primary',
});
```

**Flow** — `NotificationPrimary.java`

```java
// When creating a notification using the `show` static method,
// the duration is 5-sec by default.
Notification notification = Notification
        .show("New project plan available");
notification.addThemeVariants(NotificationVariant.LUMO_PRIMARY);
```

**React** — `notification-primary.tsx`

```tsx
const notification = Notification.show('New project plan available', {
  position: 'middle',
  duration: 0,
  theme: 'primary',
});
```

### <a id="contrast"></a>Contrast

The contrast variant can improve legibility and distinguish the notification from the rest of the UI.

**Lit** — `notification-contrast.ts`

```typescript
const notification = Notification.show('5 tasks deleted', {
  position: 'middle',
  duration: 0,
  theme: 'contrast',
});
```

**Flow** — `NotificationContrast.java`

```java
// When creating a notification using the `show` static method,
// the duration is 5-sec by default.
Notification notification = Notification.show("5 tasks deleted");
notification.addThemeVariants(NotificationVariant.LUMO_CONTRAST);
```

**React** — `notification-contrast.tsx`

```tsx
const notification = Notification.show('5 tasks deleted', {
  position: 'middle',
  duration: 0,
  theme: 'contrast',
});
```

## <a id="duration"></a>Duration

By default, notifications stay on-screen for five seconds. The duration can be set — in milliseconds — and should be done so based on the content and its importance:

- Use short durations for notifications that contain short text, are of lesser importance (e.g., operations that finished without errors), and have no interactive elements.

- Use longer durations for notifications that contain longer text, are of higher importance (e.g., errors), and have interactive elements (e.g., links or "undo" actions).

A duration of at least five seconds (i.e., `5000` milliseconds) is recommended — this is why it’s the default — to ensure that the user has a chance to read and understand the notification.

### <a id="persistent-notifications"></a>Persistent Notifications

Setting the duration to `0` milliseconds disables auto-closing. It keeps the notification visible until it’s explicitly dismissed by the user. This should be used for notifications that provide vital information to the user, such as errors.

Persistent notifications should contain a Button that closes the notification, or allows the user to take appropriate action. Less-important notifications shouldn’t be persistent, and instead disappear automatically after an appropriate delay.

## <a id="position"></a>Position

Notifications can be positioned in the viewport in seven non-stretched positions, or stretched across the top or bottom:

Use the `position` attribute in HTML templates (e.g., `<vaadin-notification position="top-end"></vaadin-notification>`).

**Lit** — `notification-position.ts`

```typescript
protected override render() {
  return html`
    <vaadin-button @click="${this.show}">top-stretch</vaadin-button>
    <vaadin-button @click="${this.show}">top-start</vaadin-button>
    <vaadin-button @click="${this.show}">top-center</vaadin-button>
    <vaadin-button @click="${this.show}">top-end</vaadin-button>
    <vaadin-button @click="${this.show}">middle</vaadin-button>
    <vaadin-button @click="${this.show}">bottom-start</vaadin-button>
    <vaadin-button @click="${this.show}">bottom-center</vaadin-button>
    <vaadin-button @click="${this.show}">bottom-end</vaadin-button>
    <vaadin-button @click="${this.show}">bottom-stretch</vaadin-button>
  `;
}

show(event: MouseEvent) {
  // Use the button label as the location
  const position = (event.target as HTMLElement).textContent as NotificationPosition;

  Notification.show(position, { position });
}
```

**Flow** — `NotificationPosition.java`

```java
add(createButton(Position.TOP_STRETCH),
        createButton(Position.TOP_START),
        createButton(Position.TOP_CENTER),
        createButton(Position.TOP_END), createButton(Position.MIDDLE),
        createButton(Position.BOTTOM_START),
        createButton(Position.BOTTOM_CENTER),
        createButton(Position.BOTTOM_END),
        createButton(Position.BOTTOM_STRETCH));

...

private Button createButton(Notification.Position position) {
    Button button = new Button(position.getClientName());
    button.addClickListener(event -> show(position));
    return button;
}

...

private void show(Notification.Position position) {
    Notification.show(position.getClientName(), 5000, position);
}
```

`notification-position-example.css`

```css
```

**React** — `notification-position.tsx`

```tsx
<Button onClick={() => show('top-stretch')}>top-stretch</Button>
<Button onClick={() => show('top-start')}>top-start</Button>
<Button onClick={() => show('top-center')}>top-center</Button>
<Button onClick={() => show('top-end')}>top-end</Button>
<Button onClick={() => show('middle')}>middle</Button>
<Button onClick={() => show('bottom-start')}>bottom-start</Button>
<Button onClick={() => show('bottom-center')}>bottom-center</Button>
<Button onClick={() => show('bottom-end')}>bottom-end</Button>
<Button onClick={() => show('bottom-stretch')}>bottom-stretch</Button>
```

**Top End** or **Bottom Start** are recommended for most notifications. They’re unobtrusive, but still noticeable. **Middle** is the most disruptive position, and should be used only for important notifications, such as errors. **Bottom End** is the least obtrusive position, but can go unnoticed. **Stretch** notifications, which span the full width of the viewport, are disruptive, and should be reserved for important notifications whose content requires more space.

Applications with a notification button, or a drop-down in the header or footer, should position notifications to appear in the same part of the screen. For a consistent user experience, use one or two positions throughout the application. Avoid using positions that may obstruct important parts of the UI, such as navigation.

## <a id="stacking"></a>Stacking

Multiple simultaneously displayed notifications are stacked vertically. The ordering of them, though, depends on their positioning.

When using the bottom half of the screen as the position, a new notification appears below the older notifications. With the position set to the top half, a new notification appears above the existing notifications.

## <a id="size"></a>Size

The notification card is automatically sized based on its content.

In large viewports, the card’s maximum width is one-third of the viewport. In small viewports, the card always consumes the entire width of the viewport.

## <a id="interactive-elements"></a>Interactive Elements

Notifications can contain interactive content (e.g., Buttons or links) that allow the user to perform related actions.

For example, if an operation fails, the error notification could offer the user the opportunity to try again. Or it could contain a link to a view that allows the user to resolve the problem.

**Lit** — `notification-retry.ts`

```html
<!-- The duration is set to 0-sec to prevent the notification from auto-close. -->
<vaadin-notification
  theme="error"
  duration="0"
  position="middle"
  .opened="${this.notificationOpened}"
  @closed="${() => {
    this.notificationOpened = false;
  }}"
  ${notificationRenderer(this.renderer, [])}
></vaadin-notification>

<!-- ... -->

renderer: NotificationLitRenderer = () => html`
  <vaadin-horizontal-layout theme="spacing" style="align-items: center;">
    <div>Failed to generate report</div>
    <vaadin-button
      theme="tertiary-inline"
      style="margin-left: var(--lumo-space-xl);"
      @click="${this.close}"
    >
      Retry
    </vaadin-button>
    <vaadin-button theme="tertiary-inline icon" @click="${this.close}" aria-label="Close">
      <vaadin-icon icon="lumo:cross"></vaadin-icon>
    </vaadin-button>
  </vaadin-horizontal-layout>
`;
```

**Flow** — `NotificationRetry.java`

```java
Notification notification = new Notification();
notification.addThemeVariants(NotificationVariant.LUMO_ERROR);

// this is the default, 0 or negative means the Notification
// is not closed automatically
notification.setDuration(0);

// Now we can compose the content from components
Button retryButton = new RetryButton();
Button closeButton = new CloseButton();

var layout = new HorizontalLayout(new Text("Failed to generate report"),
        retryButton, closeButton);
notification.add(layout);

notification.open();
```

**React** — `notification-retry.tsx`

```tsx
{/* The duration is set to 0-sec to prevent the notification from auto-close. */}
<Notification
  theme="error"
  duration={0}
  position="middle"
  opened={notificationOpened.value}
  onClosed={() => {
    notificationOpened.value = false;
  }}
>
  <HorizontalLayout theme="spacing" style={{ alignItems: 'center' }}>
    <div>Failed to generate report</div>
    <Button
      theme="tertiary-inline"
      style={{ marginLeft: 'var(--lumo-space-xl)' }}
      onClick={close}
    >
      Retry
    </Button>

    <Button theme="tertiary-inline icon" onClick={close} aria-label="Close">
      <Icon icon="lumo:cross" />
    </Button>
  </HorizontalLayout>
</Notification>
```

In situations where the user might want to revert an action, display an "Undo" button.

**Lit** — `notification-undo.ts`

```html
<vaadin-notification
  theme="contrast"
  duration="10000"
  position="middle"
  .opened="${this.notificationOpened}"
  @closed="${() => {
    this.notificationOpened = false;
  }}"
  ${notificationRenderer(this.renderer, [])}
></vaadin-notification>

<!-- ... -->

renderer: NotificationLitRenderer = () => html`
  <vaadin-horizontal-layout theme="spacing" style="align-items: center;">
    <div>5 tasks deleted</div>
    <vaadin-button
      theme="tertiary-inline"
      style="margin-left: var(--lumo-space-xl);"
      @click="${this.close}"
    >
      Undo
    </vaadin-button>
    <vaadin-button theme="tertiary-inline" aria-label="Close" @click="${this.close}">
      <vaadin-icon icon="lumo:cross"></vaadin-icon>
    </vaadin-button>
  </vaadin-horizontal-layout>
`;
```

**Flow** — `NotificationUndo.java`

```java
Notification notification = new Notification();
notification.setDuration(10000);
notification.addThemeVariants(NotificationVariant.LUMO_CONTRAST);

Button undoButton = new UndoButton();
undoButton.addClickListener(event -> {
    // In this example we just close the Notification
    notification.close();
});

var layout = new HorizontalLayout(new Text("5 tasks deleted"),
        undoButton, new CloseButton());
layout.setAlignItems(Alignment.CENTER);
notification.add(layout);

notification.open();
```

**React** — `notification-undo.tsx`

```tsx
<Notification
  theme="contrast"
  duration={10000}
  position="middle"
  opened={notificationOpened.value}
  onClosed={() => {
    notificationOpened.value = false;
  }}
>
  <HorizontalLayout theme="spacing" style={{ alignItems: 'center' }}>
    <div>5 tasks deleted</div>
    <Button
      theme="tertiary-inline"
      style={{ marginLeft: 'var(--lumo-space-xl)' }}
      onClick={close}
    >
      Undo
    </Button>

    <Button theme="tertiary-inline" aria-label="Close" onClick={close}>
      <Icon icon="lumo:cross" />
    </Button>
  </HorizontalLayout>
</Notification>
```

Notifications can also contain links to relevant information.

**Lit** — `notification-link.ts`

```html
<!-- The duration is set to 0-sec to prevent the notification from auto-close. -->
<vaadin-notification
  duration="0"
  position="middle"
  .opened="${this.notificationOpened}"
  @closed="${() => {
    this.notificationOpened = false;
  }}"
  ${notificationRenderer(this.renderer, [])}
></vaadin-notification>

<!-- ... -->

renderer: NotificationLitRenderer = () => html`
  <vaadin-horizontal-layout theme="spacing" style="align-items: center;">
    <div>Jason Bailey mentioned you in <a href="#">Project Q4</a></div>
    <vaadin-button theme="tertiary-inline" aria-label="Close" @click="${this.close}">
      <vaadin-icon icon="lumo:cross"></vaadin-icon>
    </vaadin-button>
  </vaadin-horizontal-layout>
`;
```

**Flow** — `NotificationLink.java`

```java
// When creating a notification using the constructor,
// the duration is 0-sec by default which means that
// the notification does not close automatically.
Notification notification = new Notification();

Div text = new Div(new Text("Jason Bailey mentioned you in "),
        new Anchor("#", "Project Q4"));

Button closeButton = new Button(new Icon("lumo", "cross"));
closeButton.addThemeVariants(ButtonVariant.LUMO_TERTIARY_INLINE);
closeButton.setAriaLabel("Close");
closeButton.addClickListener(event -> {
    notification.close();
});

HorizontalLayout layout = new HorizontalLayout(text, closeButton);
layout.setAlignItems(Alignment.CENTER);

notification.add(layout);
notification.open();
```

**React** — `notification-link.tsx`

```tsx
{/* The duration is set to 0-sec to prevent the notification from auto-close. */}
<Notification
  duration={0}
  position="middle"
  opened={notificationOpened.value}
  onClosed={() => {
    notificationOpened.value = false;
  }}
>
  <HorizontalLayout theme="spacing" style={{ alignItems: 'center' }}>
    <div>
      Jason Bailey mentioned you in <a href="#">Project Q4</a>
    </div>

    <Button theme="tertiary-inline" aria-label="Close" onClick={close}>
      <Icon icon="lumo:cross" />
    </Button>
  </HorizontalLayout>
</Notification>
```

### <a id="keyboard-accessible"></a>Keyboard-Accessible

Make sure that keyboard-only users can access interactive elements in notifications.

Make the notification persistent to prevent it from disappearing before the user has had a chance to react. Provide a keyboard shortcut — either to trigger the action itself or to move focus to the notification card — in cases where multiple interactive elements are present. Finally, make the shortcut discoverable, for example, by displaying it as part of the notification’s content.

**Lit** — `notification-keyboard-a11y.ts`

```typescript
protected override render() {
  return html`
    <vaadin-notification
      theme="contrast"
      duration="10000"
      position="middle"
      .opened="${this.notificationOpened}"
      @closed="${() => {
        this.notificationOpened = false;
      }}"
      ${notificationRenderer(this.renderer, [])}
    ></vaadin-notification>
  `;
}

renderer: NotificationLitRenderer = () => html`
  <vaadin-horizontal-layout style="align-items: center;">
    <div>5 tasks deleted</div>
    <vaadin-button
      style="margin-left: var(--lumo-space-xl);"
      theme="primary"
      @click="${this.close}"
    >
      Undo
      <!-- Ideally, this should be hidden if the
               device does not have a physical keyboard -->
      ${this.isMac ? '⌘' : 'Ctrl-'}Z
    </vaadin-button>
  </vaadin-horizontal-layout>
`;

connectedCallback() {
  super.connectedCallback();

  document.addEventListener('keydown', this.onKeyDown);
}

disconnectedCallback() {
  super.disconnectedCallback();

  document.removeEventListener('keydown', this.onKeyDown);
}

onKeyDown = (event: KeyboardEvent) => {
  if (this.notificationOpened && (event.metaKey || event.ctrlKey) && event.key === 'z') {
    // Handle your custom undo logic here
    // Avoid triggering the native undo action
    event.preventDefault();
    this.close();
  }
};
```

**Flow** — `NotificationKeyboardA11y.java`

```java
public Notification show() {
    Notification notification = new Notification();
    notification.setDuration(10000);
    notification.addThemeVariants(NotificationVariant.LUMO_CONTRAST);

    Div statusText = new Div(new Text("5 tasks deleted"));

    var layout = new HorizontalLayout(statusText,
            new CloseButtonWithShortcutHint());
    layout.setAlignItems(Alignment.CENTER);

    notification.add(layout);
    notification.open();

    return notification;
}

// ...

public void setupUndoShortcut(Notification notification) {
    Shortcuts.addShortcutListener(notification, notification::close,
            Key.of("z"), KeyModifier.META);
    Shortcuts.addShortcutListener(notification, notification::close,
            Key.of("z"), KeyModifier.CONTROL);
}
```

**React** — `notification-keyboard-a11y.tsx`

```tsx
useEffect(() => {
  const onKeyDown = (event: KeyboardEvent) => {
    if (notificationOpened.value && (event.metaKey || event.ctrlKey) && event.key === 'z') {
      event.preventDefault();
      close();
    }
  };

  document.addEventListener('keydown', onKeyDown);

  return () => {
    document.removeEventListener('keydown', onKeyDown);
  };
}, [notificationOpened.value]);

return (
  <>
    <Button disabled={notificationOpened.value} onClick={open}>
      Show notification
    </Button>

    <Notification
      theme="contrast"
      duration={10000}
      position="middle"
      opened={notificationOpened.value}
      onClosed={() => {
        notificationOpened.value = false;
      }}
    >
      <HorizontalLayout style={{ alignItems: 'center' }}>
        <div>5 tasks deleted</div>
        <Button style={{ marginLeft: 'var(--lumo-space-xl)' }} theme="primary" onClick={close}>
          Undo
          {isMac ? '⌘' : 'Ctrl-'}Z
        </Button>
      </HorizontalLayout>
    </Notification>
  </>
);
```

## <a id="icons-other-rich-formatting"></a>Icons & Other Rich Formatting

Icons and other content formatting can be used to provide information and helpful visual cues. For example, you might do this to make error and success notifications easier to distinguish for users with color blindness.

`notification-rich-preview.ts`

```typescript
import '@vaadin/avatar';
import '@vaadin/button';
import '@vaadin/icon';
import '@vaadin/icons';
import '@vaadin/notification';
import '@vaadin/vaadin-lumo-styles/vaadin-iconset';
import { html, LitElement } from 'lit';
import { customElement } from 'lit/decorators.js';
import { applyTheme } from 'Frontend/generated/theme';

@customElement('notification-rich-preview')
export class Example extends LitElement {
  protected override createRenderRoot() {
    const root = super.createRenderRoot();
    // Apply custom theme (only supported if your app uses one)
    applyTheme(root);
    return root;
  }

  protected override render() {
    return html`
      <vaadin-notification-card theme="success" slot="middle">
        <vaadin-horizontal-layout theme="spacing" style="align-items: center">
          <vaadin-icon icon="vaadin:check-circle"></vaadin-icon>
          <div>Application submitted!</div>
          <vaadin-button style="margin: 0 0 0 var(--lumo-space-l)">View</vaadin-button>
          <vaadin-button theme="tertiary-inline">
            <vaadin-icon icon="lumo:cross"></vaadin-icon>
          </vaadin-button>
        </vaadin-horizontal-layout>
      </vaadin-notification-card>

      <vaadin-notification-card theme="error" slot="middle">
        <vaadin-horizontal-layout theme="spacing" style="align-items: center">
          <vaadin-icon icon="vaadin:warning"></vaadin-icon>
          <div>Failed to generate report</div>
          <vaadin-button style="margin: 0 0 0 var(--lumo-space-l)">Retry</vaadin-button>
          <vaadin-button theme="tertiary-inline">
            <vaadin-icon icon="lumo:cross"></vaadin-icon>
          </vaadin-button>
        </vaadin-horizontal-layout>
      </vaadin-notification-card>

      <vaadin-notification-card slot="middle">
        <vaadin-horizontal-layout theme="spacing" style="align-items: center">
          <vaadin-avatar name="Jason Bailey"></vaadin-avatar>
          <div><b>Jason Bailey</b> mentioned you in <a href="#">Project Q4</a></div>
          <vaadin-button theme="tertiary-inline">
            <vaadin-icon icon="lumo:cross"></vaadin-icon>
          </vaadin-button>
        </vaadin-horizontal-layout>
      </vaadin-notification-card>

      <vaadin-notification-card slot="middle">
        <vaadin-horizontal-layout theme="spacing" style="align-items: center">
          <vaadin-icon
            icon="vaadin:check-circle"
            style="color: var(--lumo-success-color)"
          ></vaadin-icon>
          <div>
            <b style="color: var(--lumo-success-text-color);">Upload successful</b>
            <div
              style="font-size: var(--lumo-font-size-s); color: var(--lumo-secondary-text-color);"
            >
              <b>Financials.xlsx</b> is now available in <a href="#">Documents</a>
            </div>
          </div>
          <vaadin-button theme="tertiary-inline">
            <vaadin-icon icon="lumo:cross"></vaadin-icon>
          </vaadin-button>
        </vaadin-horizontal-layout>
      </vaadin-notification-card>
    `;
  }
}
```

**Lit** — `notification-rich.ts`

```html
<vaadin-notification
  ${notificationRenderer(
    (notification) => html`
      <vaadin-horizontal-layout theme="spacing" style="align-items: center">
        <vaadin-icon icon="vaadin:check-circle"></vaadin-icon>
        <div>Application submitted!</div>
        <vaadin-button
          style="margin: 0 0 0 var(--lumo-space-l)"
          @click="${() => notification.close()}"
        >
          View
        </vaadin-button>
        <vaadin-button
          theme="tertiary-inline"
          @click="${() => notification.close()}"
          aria-label="Close"
        >
          <vaadin-icon icon="lumo:cross"></vaadin-icon>
        </vaadin-button>
      </vaadin-horizontal-layout>
    `,
    []
  )}
  theme="success"
  position="middle"
></vaadin-notification>

<vaadin-notification
  ${notificationRenderer(
    (notification) => html`
      <vaadin-horizontal-layout theme="spacing" style="align-items: center">
        <vaadin-icon icon="vaadin:warning"></vaadin-icon>
        <div>Failed to generate report</div>
        <vaadin-button
          style="margin: 0 0 0 var(--lumo-space-l)"
          @click="${() => notification.close()}"
        >
          Retry
        </vaadin-button>
        <vaadin-button
          theme="tertiary-inline"
          @click="${() => notification.close()}"
          aria-label="Close"
        >
          <vaadin-icon icon="lumo:cross"></vaadin-icon>
        </vaadin-button>
      </vaadin-horizontal-layout>
    `,
    []
  )}
  theme="error"
  position="middle"
></vaadin-notification>

<vaadin-notification
  ${notificationRenderer(
    (notification) => html`
      <vaadin-horizontal-layout theme="spacing" style="align-items: center">
        <vaadin-avatar name="Jason Bailey"></vaadin-avatar>
        <div><b>Jason Bailey</b> mentioned you in <a href="#">Project Q4</a></div>
        <vaadin-button
          theme="tertiary-inline"
          @click="${() => notification.close()}"
          aria-label="Close"
        >
          <vaadin-icon icon="lumo:cross"></vaadin-icon>
        </vaadin-button>
      </vaadin-horizontal-layout>
    `,
    []
  )}
  position="middle"
></vaadin-notification>

<vaadin-notification
  ${notificationRenderer(
    (notification) => html`
      <vaadin-horizontal-layout theme="spacing" style="align-items: center">
        <vaadin-icon
          icon="vaadin:check-circle"
          style="color: var(--lumo-success-color)"
        ></vaadin-icon>
        <div>
          <b style="color: var(--lumo-success-text-color);">Upload successful</b>
          <div
            style="font-size: var(--lumo-font-size-s); color: var(--lumo-secondary-text-color)"
          >
            <b>Financials.xlsx</b> is now available in <a href="#">Documents</a>
          </div>
        </div>
        <vaadin-button
          theme="tertiary-inline"
          @click="${() => notification.close()}"
          aria-label="Close"
        >
          <vaadin-icon icon="lumo:cross"></vaadin-icon>
        </vaadin-button>
      </vaadin-horizontal-layout>
    `,
    []
  )}
  position="middle"
></vaadin-notification>
```

**Flow** — `NotificationRich.java`

```java
public static Notification createSubmitSuccess() {
    Notification notification = new Notification();
    notification.addThemeVariants(NotificationVariant.LUMO_SUCCESS);

    Icon icon = VaadinIcon.CHECK_CIRCLE.create();

    Button viewBtn = new Button("View", clickEvent -> notification.close());
    viewBtn.getStyle().setMargin("0 0 0 var(--lumo-space-l)");

    var layout = new HorizontalLayout(icon,
            new Text("Application submitted!"), viewBtn,
            createCloseBtn(notification));
    layout.setAlignItems(FlexComponent.Alignment.CENTER);

    notification.add(layout);

    return notification;
}

public static Notification createReportError() {
    Notification notification = new Notification();
    notification.addThemeVariants(NotificationVariant.LUMO_ERROR);

    Icon icon = VaadinIcon.WARNING.create();
    Button retryBtn = new Button("Retry",
            clickEvent -> notification.close());
    retryBtn.getStyle().setMargin("0 0 0 var(--lumo-space-l)");

    var layout = new HorizontalLayout(icon,
            new Text("Failed to generate report!"), retryBtn,
            createCloseBtn(notification));
    layout.setAlignItems(FlexComponent.Alignment.CENTER);

    notification.add(layout);

    return notification;
}

public static Notification createMentionNotification() {
    Notification notification = new Notification();

    Avatar avatar = new Avatar("Jason Bailey");

    Span name = new Span("Jason Bailey");
    name.getStyle().set("font-weight", "500");

    Div info = new Div(name, new Text(" mentioned you in "),
            new Anchor("#", "Project Q4"));

    HorizontalLayout layout = new HorizontalLayout(avatar, info,
            createCloseBtn(notification));
    layout.setAlignItems(FlexComponent.Alignment.CENTER);

    notification.add(layout);

    return notification;
}

public static Notification createUploadSuccess() {
    Notification notification = new Notification();

    Icon icon = VaadinIcon.CHECK_CIRCLE.create();
    icon.setColor("var(--lumo-success-color)");

    Div uploadSuccessful = new Div(new Text("Upload successful"));
    uploadSuccessful.getStyle().set("font-weight", "600")
            .setColor("var(--lumo-success-text-color)");

    Span fileName = new Span("Financials.xlsx");
    fileName.getStyle().set("font-size", "var(--lumo-font-size-s)")
            .set("font-weight", "600");

    Div info = new Div(uploadSuccessful,
            new Div(fileName, new Text(" is now available in "),
                    new Anchor("#", "Documents")));

    info.getStyle().set("font-size", "var(--lumo-font-size-s)")
            .setColor("var(--lumo-secondary-text-color)");

    var layout = new HorizontalLayout(icon, info,
            createCloseBtn(notification));
    layout.setAlignItems(FlexComponent.Alignment.CENTER);

    notification.add(layout);

    return notification;
}

public static Button createCloseBtn(Notification notification) {
    Button closeBtn = new Button(VaadinIcon.CLOSE_SMALL.create(),
            clickEvent -> notification.close());
    closeBtn.addThemeVariants(LUMO_TERTIARY_INLINE);

    return closeBtn;
}
```

**React** — `notification-rich.tsx`

```tsx
<Notification
  theme="success"
  position="middle"
  opened={openedNotifications.value.includes(0)}
  onClosed={() => {
    close(0);
  }}
>
  <HorizontalLayout theme="spacing" style={{ alignItems: 'center' }}>
    <Icon icon="vaadin:check-circle" />
    <div>Application submitted!</div>
    <Button style={{ margin: '0 0 0 var(--lumo-space-l)' }} onClick={() => close(0)}>
      View
    </Button>
    <Button theme="tertiary-inline" onClick={() => close(0)} aria-label="Close">
      <Icon icon="lumo:cross" />
    </Button>
  </HorizontalLayout>
</Notification>

<Notification
  theme="error"
  position="middle"
  opened={openedNotifications.value.includes(1)}
  onClosed={() => {
    close(1);
  }}
>
  <HorizontalLayout theme="spacing" style={{ alignItems: 'center' }}>
    <Icon icon="vaadin:warning" />
    <div>Failed to generate report</div>
    <Button style={{ margin: '0 0 0 var(--lumo-space-l)' }} onClick={() => close(1)}>
      Retry
    </Button>
    <Button theme="tertiary-inline" onClick={() => close(1)} aria-label="Close">
      <Icon icon="lumo:cross" />
    </Button>
  </HorizontalLayout>
</Notification>

<Notification
  position="middle"
  opened={openedNotifications.value.includes(2)}
  onClosed={() => {
    close(2);
  }}
>
  <HorizontalLayout theme="spacing" style={{ alignItems: 'center' }}>
    <Avatar name="Jason Bailey" />
    <div>
      <b>Jason Bailey</b> mentioned you in <a href="#">Project Q4</a>
    </div>
    <Button theme="tertiary-inline" onClick={() => close(2)} aria-label="Close">
      <Icon icon="lumo:cross" />
    </Button>
  </HorizontalLayout>
</Notification>

<Notification
  position="middle"
  opened={openedNotifications.value.includes(3)}
  onClosed={() => {
    close(3);
  }}
>
  <HorizontalLayout theme="spacing" style={{ alignItems: 'center' }}>
    <Icon icon="vaadin:check-circle" style={{ color: 'var(--lumo-success-color)' }} />
    <div>
      <b style={{ color: 'var(--lumo-success-text-color)' }}>Upload successful</b>
      <div
        style={{
          fontSize: 'var(--lumo-font-size-s)',
          color: 'var(--lumo-secondary-text-color)',
        }}
      >
        <b>Financials.xlsx</b> is now available in <a href="#">Documents</a>
      </div>
    </div>
    <Button theme="tertiary-inline" onClick={() => close(3)} aria-label="Close">
      <Icon icon="lumo:cross" />
    </Button>
  </HorizontalLayout>
</Notification>
```

## <a id="static-helper"></a>Static Helper

For simple, one-off notifications, it’s convenient to use the static `show()` helper method. The helper manages the notification’s lifecycle, and adds and removes it from the DOM, automatically.

**Lit** — `notification-static-helper.ts`

```typescript
// Show a simple text-based notification
Notification.show('Financial report generated', {
  position: 'middle',
});
// Show a notification with markup using a Lit template
Notification.show(
  html`
    <b>@John:</b>
    &nbsp;
    <span>
      How about lunch at
      <span style="color: var(--lumo-primary-text-color)">12:30pm</span>?
    </span>
  `,
  {
    position: 'middle',
  }
);
```

**Flow** — `NotificationStaticHelper.java`

```java
// Show a simple text-based notification
Notification notification = Notification
        .show("Financial report generated");
notification.setPosition(Notification.Position.MIDDLE);
```

**React** — `notification-static-helper.tsx`

```tsx
// Show a simple text-based notification
Notification.show('Financial report generated', {
  position: 'middle',
});
```

## <a id="best-practices"></a>Best Practices

### <a id="use-sparingly"></a>Use Sparingly

Notifications are disruptive by design and should be used sparingly. Use fewer notifications by reserving them for more-important information that might otherwise go unnoticed by the user.

Less-urgent notifications can be provided through a link or a drop-down in the application header or footer, instead of via immediate notifications.

**Lit** — `notification-popup.ts`

```typescript
protected override render() {
  return html`
    <vaadin-context-menu
      open-on="click"
      ${contextMenuRenderer(
        () => html`<div style="padding: var(--lumo-space-l);">Show notifications here</div>`,
        []
      )}
    >
      <vaadin-button aria-label="notifications" theme="tertiary">
        <vaadin-icon icon="vaadin:bell-o"></vaadin-icon>
        <span theme="badge error primary small pill">4</span>
      </vaadin-button>
    </vaadin-context-menu>
  `;
}
```

**Flow** — `NotificationPopup.java`

```java
var bellBtn = new MessagesButton();
bellBtn.setUnreadMessages(4);

ContextMenu menu = new ContextMenu();
menu.setOpenOnClick(true);
menu.setTarget(bellBtn);
menu.addItem("This is ContextMenu");
menu.addItem("Consider Using");
menu.addItem("ContextMenu");
menu.addItem("Instead of Notifications");
```

**React** — `notification-popup.tsx`

```tsx
<ContextMenu
  openOn="click"
  renderer={() => <div style={{ padding: 'var(--lumo-space-l)' }}>Show notifications here</div>}
>
  <Button aria-label="notifications" theme="tertiary">
    <Icon icon="vaadin:bell-o" />
    <span style={{ position: 'absolute', transform: 'translate(-40%, -30%)' }}>
      <span {...{ theme: 'badge error primary small pill' }}>4</span>
    </span>
  </Button>
</ContextMenu>
```

### <a id="limit-content-length"></a>Limit Content Length

Notifications should be brief and to the point. Try to limit the content to one or two lines. More information can be provided through an embedded link or Button.

Show a preview of longer content, and link to the full details.

`notification-content-length-do.ts`

```typescript
import '@vaadin/button';
import '@vaadin/icon';
import '@vaadin/notification';
import '@vaadin/vaadin-lumo-styles/vaadin-iconset';
import { html, LitElement } from 'lit';
import { applyTheme } from 'Frontend/generated/theme';

export class Example extends LitElement {
  protected override createRenderRoot() {
    const root = super.createRenderRoot();
    // Apply custom theme (only supported if your app uses one)
    applyTheme(root);
    return root;
  }

  protected override render() {
    return html`
      <vaadin-notification-card slot="middle">
        <div>
          <div>Aria Bailey</div>
          <div style="font-size: var(--lumo-font-size-s); color: var(--lumo-secondary-text-color);">
            Yeah, I know. But could you help me with...
          </div>
        </div>
        <vaadin-button>View</vaadin-button>
        <vaadin-button theme="tertiary-inline">
          <vaadin-icon icon="lumo:cross"></vaadin-icon>
        </vaadin-button>
      </vaadin-notification-card>
    `;
  }
}
```

Don’t display all details in the notification.

`notification-content-length-dont.ts`

```typescript
import '@vaadin/button';
import '@vaadin/icon';
import '@vaadin/notification';
import '@vaadin/vaadin-lumo-styles/vaadin-iconset';
import { html, LitElement } from 'lit';
import { applyTheme } from 'Frontend/generated/theme';

export class Example extends LitElement {
  protected override createRenderRoot() {
    const root = super.createRenderRoot();
    // Apply custom theme (only supported if your app uses one)
    applyTheme(root);
    return root;
  }

  protected override render() {
    return html`
      <vaadin-notification-card slot="middle">
        <div>
          <div>New message from Aria Bailey</div>
          <div style="font-size: var(--lumo-font-size-s); color: var(--lumo-secondary-text-color);">
            Yeah, I know. But could you help me with this. I’m not sure where the bug is in my CSS?
            The checkmark doesn’t get the right color. I’m trying to use the CSS custom properties
            from our design system, but for some reason it’s not working.
          </div>
        </div>
        <vaadin-button theme="tertiary-inline">
          <vaadin-icon icon="lumo:cross"></vaadin-icon>
        </vaadin-button>
      </vaadin-notification-card>
    `;
  }
}
```

## <a id="related-components"></a>Related Components

| Component                                                  | Usage Recommendation                                                                            |
| ---------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
| [Dialog](https://vaadin.com/docs/v24/components/dialog.md) | Dialog should be used instead of Notification for anything that requires immediate user action. |

`77E15AD0-F634-4D81-8C4D-FAC7A21D889B`
