Documentation

Documentation versions (currently viewingVaadin 23)

Dialog

Dialog is a small window that can be used to present information and user interface elements in an overlay.

Open in a
new tab

The inline .renderer() function is encapsulated by the guard directive to avoid reassigning the renderer property every time the view is rendered. Basically, the renderer function is cached, improving performance.

<vaadin-dialog
  header-title="New employee"
  .opened="${this.dialogOpened}"
  @opened-changed="${(e: CustomEvent) => (this.dialogOpened = e.detail.value)}"
  .footerRenderer="${guard([], () => (root: HTMLElement) => {
    render(this.footerLayout, root);
  })}"
  .renderer="${guard([], () => (root: HTMLElement) => {
    render(this.dialogLayout, root);
  })}"
></vaadin-dialog>

<vaadin-button @click="${() => (this.dialogOpened = true)}">Show dialog</vaadin-button>

Structure

The Dialog component has static header and footer areas, and a scrolling content area in between. The header and footer are optional, and automatically hidden if empty and not explicitly enabled.

The header contains an optional title element, and a slot next to it for custom header content, such as a close button.

Open in a
new tab

The inline .renderer() function is encapsulated by the guard directive to avoid reassigning the renderer property every time the view is rendered. Basically, the renderer function is cached, improving performance.

<vaadin-dialog
  header-title="User details"
  .headerRenderer="${guard([], () => (root: HTMLElement) => {
    render(
      html`
        <vaadin-button theme="tertiary" @click="${() => (this.dialogOpened = false)}">
          <vaadin-icon icon="lumo:cross"></vaadin-icon>
        </vaadin-button>
      `,
      root
    );
  })}"
  .opened="${this.dialogOpened}"
  @opened-changed="${(e: CustomEvent) => (this.dialogOpened = e.detail.value)}"
  .renderer="${guard(this.user, () => this.dialogRenderer)}"
></vaadin-dialog>

Buttons for closure actions, such as Save, Cancel, Delete, and so on, should be placed in the footer. See the Button component for guidelines for the placement of buttons in dialogs. Footer content is right-aligned by default. Components can be left-aligned by applying a margin:

Open in a
new tab

The inline .renderer() function is encapsulated by the guard directive to avoid reassigning the renderer property every time the view is rendered. Basically, the renderer function is cached, improving performance.

<vaadin-dialog
  header-title="${`Delete user "${this.user?.firstName} ${this.user?.lastName}"?`}"
  .footerRenderer="${guard([], () => (root: HTMLElement) => {
    render(
      html`
        <vaadin-button
          theme="primary error"
          @click="${() => (this.dialogOpened = false)}"
          style="margin-right: auto;"
        >
          Delete
        </vaadin-button>
        <vaadin-button theme="tertiary" @click="${() => (this.dialogOpened = false)}"
          >Cancel</vaadin-button
        >
      `,
      root
    );
  })}"
  .opened="${this.dialogOpened}"
  @opened-changed="${(e: CustomEvent) => (this.dialogOpened = e.detail.value)}"
  .renderer="${guard([], () => (root: HTMLElement) => {
    render(html`Are you sure you want to delete this user permanently?`, root);
  })}"
></vaadin-dialog>

Padding

The content area’s built-in padding can be removed by applying the no-padding theme variant.

Open in a
new tab

The inline .renderer() function is encapsulated by the guard directive to avoid reassigning the renderer property every time the view is rendered. Basically, the renderer function is cached, improving performance.

<vaadin-dialog
  theme="no-padding"
  header-title="Filter reports by users:"
  .opened="${this.dialogOpened}"
  @opened-changed="${(e: CustomEvent) => (this.dialogOpened = e.detail.value)}"
  .renderer="${guard([], () => (root: HTMLElement) => {
    render(
      html`
        <vaadin-grid .items="${this.people}" style="width: 500px; max-width: 100%;">
          <vaadin-grid-selection-column></vaadin-grid-selection-column>
          <vaadin-grid-column
            header="Name"
            .renderer="${(
              root: HTMLElement,
              _: GridColumn<Person>,
              model: GridItemModel<Person>
            ) => {
              render(html`${model.item.firstName} ${model.item.lastName}`, root);
            }}"
          ></vaadin-grid-column>
        </vaadin-grid>
      `,
      root
    );
  })}"
  .footerRenderer="${guard([], () => (root: HTMLElement) => {
    render(
      html`
        <vaadin-button theme="primary" @click="${() => (this.dialogOpened = false)}">
          Filter
        </vaadin-button>
      `,
      root
    );
  })}"
></vaadin-dialog>

Modality

A modal dialog blocks the user from interacting with the rest of the user interface while the dialog is open. A non-modal dialog, on the other hand, does not block interaction.

Dialogs are modal by default.

Use modal dialogs for:

  • displaying important information, like system errors

  • requesting user input as part of a workflow, for example an edit dialog

  • confirmation of irreversible actions, such as deleting data (Confirm Dialog is a convenient alternative for these use cases)

  • breaking out sub-tasks into a separate user interface

Important
Modal parent dialogs in Flow
See the Technical section for details on the behavior of modal dialogs in Vaadin Flow.

Use non-modal dialogs:

  • when the user needs access to the content below the dialog

  • for less critical, optional, and/or support tasks

<vaadin-dialog modeless>...</vaadin-dialog>

In most cases, non-modal dialogs should be draggable, so that the user can move them to access the user interface beneath.

Draggable

Dialogs can be made draggable, enabling the user to move them around using a pointing device.

By default, the outer edges of a dialog, as well as the space between its components, can be used to move the dialog around.

The default areas from which a dialog can be dragged depend on whether the built-in header is used:

  • If the built-in header or footer is used, they function as the dialog’s default drag handles

  • Without the built-in header, any empty space within the dialog functions as a drag handle

Any component contained within a dialog can be marked and used as a drag handle by applying the draggable class name to it. You can choose whether or not to make the component’s content draggable as well, or just the component itself.

Open in a
new tab

The inline .renderer() function is encapsulated by the guard directive to avoid reassigning the renderer property every time the view is rendered. Basically, the renderer function is cached, improving performance.

<vaadin-dialog
  aria-label="Add note"
  draggable
  .headerRenderer="${guard([], () => (root: HTMLElement) => {
    render(
      html`
        <h2
          class="draggable"
          style="flex: 1; cursor: move; margin: 0; font-size: 1.5em; font-weight: bold; padding: var(--lumo-space-m) 0;"
        >
          Add note
        </h2>
      `,
      root
    );
  })}"
  modeless
  .opened="${this.dialogOpened}"
  @opened-changed="${(e: CustomEvent) => (this.dialogOpened = e.detail.value)}"
  .renderer="${guard([], () => (root: HTMLElement) => {
    render(
      html`
        <vaadin-vertical-layout
          theme="spacing"
          style="width: 300px; max-width: 100%; align-items: stretch;"
        >
          <vaadin-vertical-layout style="align-items: stretch;">
            <vaadin-text-field label="Title"></vaadin-text-field>
            <vaadin-text-area label="Description"></vaadin-text-area>
          </vaadin-vertical-layout>
        </vaadin-vertical-layout>
      `,
      root
    );
  })}"
  .footerRenderer="${guard([], () => (root: HTMLElement) => {
    render(
      html`<vaadin-button @click="${() => (this.dialogOpened = false)}">
          Cancel
        </vaadin-button>
        <vaadin-button theme="primary" @click="${() => (this.dialogOpened = false)}">
          Add note
        </vaadin-button>`,
      root
    );
  })}"
></vaadin-dialog>

It is recommended to make non-modal dialogs draggable, so that the user can interact with content that might otherwise be obscured by the Dialog. For example, a Dialog for taking notes or for adding widgets to a dashboard using drag and drop can offer a better experience by allowing the user to move the Dialog around.

Modal dialogs do not benefit from being draggable, as their modality curtain (the dark overlay behind the dialog) obscures the underlying user interface.

Resizable

A resizable dialog allows the user to resize the Dialog by dragging from the edges of the Dialog with a pointing device. Dialogs are not resizable by default.

Dialogs containing dynamic content and/or a lot of information, such as complex forms or Grids, may benefit from being resizable, as this offers the user some flexibility as to how much data is visible at once. It also gives the user control over which part of the underlying user interface is obscured.

Dialogs that contain very little, or compact, information do not need to be resizable.

Open in a
new tab

The inline .renderer() function is encapsulated by the guard directive to avoid reassigning the renderer property every time the view is rendered. Basically, the renderer function is cached, improving performance.

<vaadin-dialog
  header-title="Employee list"
  resizable
  draggable
  .opened="${this.dialogOpened}"
  @opened-changed="${(e: CustomEvent) => (this.dialogOpened = e.detail.value)}"
  .renderer="${guard([], () => (root: HTMLElement) => {
    render(
      html`
        <vaadin-vertical-layout
          theme="spacing"
          style="max-width: 100%; min-width: 300px; height: 100%; align-items: stretch;"
        >
          <vaadin-grid .items="${this.people}">
            <vaadin-grid-column path="firstName" title="First name"></vaadin-grid-column>
            <vaadin-grid-column path="lastName" title="Last name"></vaadin-grid-column>
            <vaadin-grid-column path="email" title="Email"></vaadin-grid-column>
            <vaadin-grid-column path="profession" title="Profession"></vaadin-grid-column>
            <vaadin-grid-column path="membership" title="Membership"></vaadin-grid-column>
          </vaadin-grid>
        </vaadin-vertical-layout>
      `,
      root
    );
  })}"
></vaadin-dialog>

Closing

Modal dialogs are closable in three ways:

  1. Pressing the Esc key

  2. Clicking outside the Dialog

  3. Programmatically, for example through the click of a Button

Providing an explicit button for closing a Dialog is recommended.

Open in a
new tab

The inline .renderer() function is encapsulated by the guard directive to avoid reassigning the renderer property every time the view is rendered. Basically, the renderer function is cached, improving performance.

<vaadin-dialog
  aria-label="System maintenance notice"
  .opened="${this.dialogOpened}"
  @opened-changed="${(e: CustomEvent) => (this.dialogOpened = e.detail.value)}"
  .renderer="${guard([], () => (root: HTMLElement) => {
    render(
      html`
        <vaadin-vertical-layout
          theme="spacing"
          style="width: 300px; max-width: 100%; align-items: stretch;"
        >
          <h2 style="margin: var(--lumo-space-m) 0; font-size: 1.5em; font-weight: bold;">
            System maintenance
          </h2>
          <p>
            System maintenance will begin at 3 PM. It is schedule to conclude at 5PM. We
            apologise for any inconvenience.
          </p>
          <vaadin-button
            @click="${() => (this.dialogOpened = false)}"
            style="align-self: flex-end;"
          >
            Close
          </vaadin-button>
        </vaadin-vertical-layout>
      `,
      root
    );
  })}"
></vaadin-dialog>

Best Practices

Use Sparingly

Dialogs are disruptive by nature and should be used sparingly. Do not use them to communicate non-essential information, such as success messages like “Logged in”, “Copied”, and so on. Instead, use Notifications when appropriate.

Button Placement

ComponentUsage recommendations

Confirm Dialog

Dialog for confirming user actions and decisions

Technical

Multiple Dialogs and Server-side Modality in Flow

By default, opening multiple Dialog components simultaneously in Flow adds each subsequent Dialog as a child of the previous one.

Due to the server-side modality mechanism in Flow, this means that closing a modal Dialog automatically also closes all its children, that is, any subsequent Dialogs opened after the modal.

This can be avoided by explicitly adding each Dialog to the UI before opening it:

Dialog d1 = new Dialog();
Dialog d2 = new Dialog();
add(d1, d2);  // Add dialogs to the UI before opening
d1.open();
d2.open();