UWC Components
  • Default
  • Material
  • Fluent

On this page

Popover

A structured click-triggered popover with optional header, footer, close button, and placement arrow. For a blank-canvas panel use uwc-overlay instead.

uwc-popover is a structured floating panel with optional header, footer, close button, and directional arrow. Place a trigger element in slot="trigger" and body content in slot="content". For a free-form panel use uwc-overlay instead.

Import

All components

import '@uwc/components';

Selected component (Lit / Angular / Vue)

import { UwcPopover } from '@uwc/components/popover';
customElements.define('uwc-popover', UwcPopover);

React

import { UwcPopover } from '@uwc/components/react';

Usage

Lit

import { LitElement, html } from 'lit';
import { customElement } from 'lit/decorators.js';

@customElement('app-demo')
export class AppDemo extends LitElement {
  render() {
    return html`
      <uwc-popover header="My Popover" placement="bottom-start">
        <uwc-button slot="trigger" label="Open"></uwc-button>
        <div slot="content">Popover body content.</div>
      </uwc-popover>
    `;
  }
}

React

import React from 'react';
import { UwcPopover } from '@uwc/components/react';

export default function App() {
  return (
    <UwcPopover header="My Popover" placement="bottom-start">
      <uwc-button slot="trigger" label="Open"></uwc-button>
      <div slot="content">Popover body content.</div>
    </UwcPopover>
  );
}

Angular

import { Component } from '@angular/core';
import '@uwc/popover';

@Component({
  selector: 'app-root',
  standalone: true,
  template: `
    <uwc-popover header="My Popover" placement="bottom-start">
      <uwc-button slot="trigger" label="Open"></uwc-button>
      <div slot="content">Popover body content.</div>
    </uwc-popover>
  `,
})
export class AppComponent {}

Vue

import '@uwc/popover';

export default {
  template: `
    <uwc-popover header="My Popover" placement="bottom-start">
      <uwc-button slot="trigger" label="Open"></uwc-button>
      <div slot="content">Popover body content.</div>
    </uwc-popover>
  `,
};

Basic Usage

import { LitElement, html } from 'lit';
import { customElement } from 'lit/decorators.js';

@customElement('app-demo')
export class AppDemo extends LitElement {
  render() {
    return html`
      <div style="display:flex;gap:1rem;flex-wrap:wrap;">
        <uwc-popover header="Account info" placement="bottom-start">
          <uwc-button slot="trigger" label="Open popover"></uwc-button>
          <div slot="content" style="padding:1rem;min-width:220px;font-size:.875rem;color:#374151;">
            Manage your account settings and preferences here.
          </div>
        </uwc-popover>
        <uwc-popover header="With close" show-close-button arrow placement="bottom">
          <uwc-button slot="trigger" label="With close button"></uwc-button>
          <div slot="content" style="padding:1rem;min-width:220px;font-size:.875rem;">
            Click the <strong>✕</strong> or outside to dismiss.
          </div>
        </uwc-popover>
      </div>
    `;
  }
}
import React from 'react';
import { UwcPopover } from '@uwc/components/react';

export default function App() {
  return (
    <div style={{ display: 'flex', gap: '1rem', flexWrap: 'wrap' }}>
      <UwcPopover header="Account info" placement="bottom-start">
        <uwc-button slot="trigger" label="Open popover"></uwc-button>
        <div slot="content" style={{ padding: '1rem', minWidth: '220px', fontSize: '.875rem', color: '#374151' }}>
          Manage your account settings and preferences here.
        </div>
      </UwcPopover>
      <UwcPopover header="With close" showCloseButton arrow placement="bottom">
        <uwc-button slot="trigger" label="With close button"></uwc-button>
        <div slot="content" style={{ padding: '1rem', minWidth: '220px', fontSize: '.875rem' }}>
          Click the <strong></strong> or outside to dismiss.
        </div>
      </UwcPopover>
    </div>
  );
}
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  standalone: true,
  template: `
    <div style="display:flex;gap:1rem;flex-wrap:wrap;">
      <uwc-popover header="Account info" placement="bottom-start">
        <uwc-button slot="trigger" label="Open popover"></uwc-button>
        <div slot="content" style="padding:1rem;min-width:220px;font-size:.875rem;color:#374151;">
          Manage your account settings and preferences here.
        </div>
      </uwc-popover>
      <uwc-popover header="With close" show-close-button arrow placement="bottom">
        <uwc-button slot="trigger" label="With close button"></uwc-button>
        <div slot="content" style="padding:1rem;min-width:220px;font-size:.875rem;">
          Click the ✕ or outside to dismiss.
        </div>
      </uwc-popover>
    </div>
  `
})
export class AppComponent {}
export default {
  template: `
    <div style="display:flex;gap:1rem;flex-wrap:wrap;">
      <uwc-popover header="Account info" placement="bottom-start">
        <uwc-button slot="trigger" label="Open popover"></uwc-button>
        <div slot="content" style="padding:1rem;min-width:220px;font-size:.875rem;color:#374151;">
          Manage your account settings and preferences here.
        </div>
      </uwc-popover>
      <uwc-popover header="With close" show-close-button arrow placement="bottom">
        <uwc-button slot="trigger" label="With close button"></uwc-button>
        <div slot="content" style="padding:1rem;min-width:220px;font-size:.875rem;">
          Click the ✕ or outside to dismiss.
        </div>
      </uwc-popover>
    </div>
  `
};
Manage your account settings and preferences here.
Click the or outside to dismiss.

Basic

Use slot="trigger" for the activating element and slot="content" for the body. Set header for a plain-text title.

import { LitElement, html } from 'lit';
import { customElement } from 'lit/decorators.js';

@customElement('popover-basic-demo')
export class AppDemo extends LitElement {
  render() {
    return html`
      <uwc-popover header="Account info" placement="bottom-start">
        <uwc-button slot="trigger" label="Open popover"></uwc-button>
        <div slot="content" style="padding:1rem;min-width:240px;font-size:.875rem;color:#374151;">
          Manage your account settings and preferences here.
        </div>
      </uwc-popover>
    `;
  }
}
import React from 'react';
import { UwcPopover } from '@uwc/components/react';

export default function App() {
  return (
    <UwcPopover header="Account info" placement="bottom-start">
      <uwc-button slot="trigger" label="Open popover"></uwc-button>
      <div slot="content" style={{ padding: '1rem', minWidth: '240px', fontSize: '.875rem', color: '#374151' }}>
        Manage your account settings and preferences here.
      </div>
    </UwcPopover>
  );
}
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  standalone: true,
  template: `
    <uwc-popover header="Account info" placement="bottom-start">
      <uwc-button slot="trigger" label="Open popover"></uwc-button>
      <div slot="content" style="padding:1rem;min-width:240px;font-size:.875rem;color:#374151;">
        Manage your account settings and preferences here.
      </div>
    </uwc-popover>
  `
})
export class AppComponent {}
export default {
  template: `
    <uwc-popover header="Account info" placement="bottom-start">
      <uwc-button slot="trigger" label="Open popover"></uwc-button>
      <div slot="content" style="padding:1rem;min-width:240px;font-size:.875rem;color:#374151;">
        Manage your account settings and preferences here.
      </div>
    </uwc-popover>
  `
};
Manage your account settings and preferences here.

With close button and arrow

Add show-close-button for a ✕ dismiss button and arrow for a directional indicator.

import { LitElement, html } from 'lit';
import { customElement } from 'lit/decorators.js';

@customElement('popover-close-demo')
export class AppDemo extends LitElement {
  render() {
    return html`
      <uwc-popover header="Help" show-close-button arrow placement="bottom">
        <uwc-button slot="trigger" label="Open with close"></uwc-button>
        <div slot="content" style="padding:1rem;min-width:240px;font-size:.875rem;color:#374151;">
          Click <strong>✕</strong>, click outside, or press <kbd>Escape</kbd> to close.
        </div>
      </uwc-popover>
    `;
  }
}
import React from 'react';
import { UwcPopover } from '@uwc/components/react';

export default function App() {
  return (
    <UwcPopover header="Help" showCloseButton arrow placement="bottom">
      <uwc-button slot="trigger" label="Open with close"></uwc-button>
      <div slot="content" style={{ padding: '1rem', minWidth: '240px', fontSize: '.875rem', color: '#374151' }}>
        Click <strong></strong>, click outside, or press <kbd>Escape</kbd> to close.
      </div>
    </UwcPopover>
  );
}
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  standalone: true,
  template: `
    <uwc-popover header="Help" show-close-button arrow placement="bottom">
      <uwc-button slot="trigger" label="Open with close"></uwc-button>
      <div slot="content" style="padding:1rem;min-width:240px;font-size:.875rem;color:#374151;">
        Click ✕, click outside, or press Escape to close.
      </div>
    </uwc-popover>
  `
})
export class AppComponent {}
export default {
  template: `
    <uwc-popover header="Help" show-close-button arrow placement="bottom">
      <uwc-button slot="trigger" label="Open with close"></uwc-button>
      <div slot="content" style="padding:1rem;min-width:240px;font-size:.875rem;color:#374151;">
        Click ✕, click outside, or press Escape to close.
      </div>
    </uwc-popover>
  `
};
Click , click outside, or press Escape to close.

With footer

Use slot="footer" to render action buttons or supplemental content below the body.

import { LitElement, html } from 'lit';
import { customElement } from 'lit/decorators.js';

@customElement('popover-footer-demo')
export class AppDemo extends LitElement {
  render() {
    return html`
      <uwc-popover header="Confirm action" show-close-button placement="bottom-start">
        <uwc-button slot="trigger" label="Delete item"></uwc-button>
        <div slot="content" style="padding:1rem;min-width:260px;font-size:.875rem;color:#374151;">
          Are you sure you want to delete this item? This action cannot be undone.
        </div>
        <div slot="footer" style="display:flex;justify-content:flex-end;gap:.5rem;padding:.75rem 1rem;">
          <uwc-button variant="secondary" label="Cancel" size="small"></uwc-button>
          <uwc-button label="Delete" size="small"></uwc-button>
        </div>
      </uwc-popover>
    `;
  }
}
import React from 'react';
import { UwcPopover } from '@uwc/components/react';

export default function App() {
  return (
    <UwcPopover header="Confirm action" showCloseButton placement="bottom-start">
      <uwc-button slot="trigger" label="Delete item"></uwc-button>
      <div slot="content" style={{ padding: '1rem', minWidth: '260px', fontSize: '.875rem', color: '#374151' }}>
        Are you sure you want to delete this item? This action cannot be undone.
      </div>
      <div slot="footer" style={{ display: 'flex', justifyContent: 'flex-end', gap: '.5rem', padding: '.75rem 1rem' }}>
        <uwc-button variant="secondary" label="Cancel" size="small"></uwc-button>
        <uwc-button label="Delete" size="small"></uwc-button>
      </div>
    </UwcPopover>
  );
}
import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  standalone: true,
  template: `
    <uwc-popover header="Confirm action" show-close-button placement="bottom-start">
      <uwc-button slot="trigger" label="Delete item"></uwc-button>
      <div slot="content" style="padding:1rem;min-width:260px;font-size:.875rem;color:#374151;">
        Are you sure you want to delete this item? This action cannot be undone.
      </div>
      <div slot="footer" style="display:flex;justify-content:flex-end;gap:.5rem;padding:.75rem 1rem;">
        <uwc-button variant="secondary" label="Cancel" size="small"></uwc-button>
        <uwc-button label="Delete" size="small"></uwc-button>
      </div>
    </uwc-popover>
  `
})
export class AppComponent {}
export default {
  template: `
    <uwc-popover header="Confirm action" show-close-button placement="bottom-start">
      <uwc-button slot="trigger" label="Delete item"></uwc-button>
      <div slot="content" style="padding:1rem;min-width:260px;font-size:.875rem;color:#374151;">
        Are you sure you want to delete this item? This action cannot be undone.
      </div>
      <div slot="footer" style="display:flex;justify-content:flex-end;gap:.5rem;padding:.75rem 1rem;">
        <uwc-button variant="secondary" label="Cancel" size="small"></uwc-button>
        <uwc-button label="Delete" size="small"></uwc-button>
      </div>
    </uwc-popover>
  `
};
Are you sure you want to delete this item? This action cannot be undone.

Listening to events

uwc-show, uwc-hide, and uwc-close (close button click) fire on panel state changes.

import { LitElement, html, css } from 'lit';
import { customElement, state } from 'lit/decorators.js';

@customElement('popover-events-demo')
export class AppDemo extends LitElement {
  static styles = css`
    :host { display: block; }
    .log { margin-top:.75rem;padding:.6rem .75rem;border-radius:6px;background:#f9fafb;border:1px solid #e5e7eb;font-size:.8rem;font-family:monospace;min-height:2.5rem;color:#374151; }
  `;

  @state() private _log: string[] = [];

  private _record(event: string) {
    const time = new Date().toLocaleTimeString();
    this._log = [`[${time}] ${event}`, ...this._log].slice(0, 5);
  }

  render() {
    return html`
      <uwc-popover
        header="Events demo"
        show-close-button
        placement="bottom-start"
        @uwc-show=${() => this._record('uwc-show')}
        @uwc-hide=${() => this._record('uwc-hide')}
        @uwc-close=${() => this._record('uwc-close')}>
        <uwc-button slot="trigger" label="Toggle popover"></uwc-button>
        <div slot="content" style="padding:1rem;min-width:220px;font-size:.875rem;">Open and close to see events.</div>
      </uwc-popover>
      <div class="log">
        ${this._log.length
          ? this._log.map(l => html`<div>${l}</div>`)
          : html`<span style="color:#9ca3af">Interact with the popover to see events...</span>`}
      </div>
    `;
  }
}
import React, { useState } from 'react';
import { UwcPopover } from '@uwc/components/react';

export default function App() {
  const [log, setLog] = useState([]);
  const record = (event) => {
    const time = new Date().toLocaleTimeString();
    setLog(prev => [`[${time}] ${event}`, ...prev].slice(0, 5));
  };
  return (
    <>
      <UwcPopover
        header="Events demo"
        showCloseButton
        placement="bottom-start"
        onUwcShow={() => record('uwc-show')}
        onUwcHide={() => record('uwc-hide')}
        onUwcClose={() => record('uwc-close')}
      >
        <uwc-button slot="trigger" label="Toggle popover"></uwc-button>
        <div slot="content" style={{ padding: '1rem', minWidth: '220px', fontSize: '.875rem' }}>Open and close to see events.</div>
      </UwcPopover>
      <div style={{ marginTop: '.75rem', fontFamily: 'monospace', fontSize: '.8rem' }}>
        {log.length
          ? log.map((l, i) => <div key={i}>{l}</div>)
          : <span style={{ color: '#9ca3af' }}>Interact with the popover to see events...</span>}
      </div>
    </>
  );
}
import { Component, signal } from '@angular/core';

@Component({
  selector: 'app-root',
  standalone: true,
  template: `
    <uwc-popover
      header="Events demo"
      show-close-button
      placement="bottom-start"
      (uwc-show)="record('uwc-show')"
      (uwc-hide)="record('uwc-hide')"
      (uwc-close)="record('uwc-close')">
      <uwc-button slot="trigger" label="Toggle popover"></uwc-button>
      <div slot="content" style="padding:1rem;min-width:220px;font-size:.875rem;">Open and close to see events.</div>
    </uwc-popover>
    <div style="margin-top:.75rem;font-family:monospace;font-size:.8rem;">
      @for (l of log(); track l) {
        <div>{{ l }}</div>
      }
      @if (!log().length) {
        <span style="color:#9ca3af">Interact with the popover to see events...</span>
      }
    </div>
  `
})
export class AppComponent {
  readonly log = signal<string[]>([]);
  record(event: string) {
    const time = new Date().toLocaleTimeString();
    this.log.set([`[${time}] ${event}`, ...this.log()].slice(0, 5));
  }
}
export default {
  data() {
    return { log: [] };
  },
  methods: {
    record(event) {
      const time = new Date().toLocaleTimeString();
      this.log = [`[${time}] ${event}`, ...this.log].slice(0, 5);
    },
  },
  template: `
    <uwc-popover
      header="Events demo"
      show-close-button
      placement="bottom-start"
      @uwc-show="record('uwc-show')"
      @uwc-hide="record('uwc-hide')"
      @uwc-close="record('uwc-close')">
      <uwc-button slot="trigger" label="Toggle popover"></uwc-button>
      <div slot="content" style="padding:1rem;min-width:220px;font-size:.875rem;">Open and close to see events.</div>
    </uwc-popover>
    <div style="margin-top:.75rem;font-family:monospace;font-size:.8rem;">
      <div v-for="l in log" :key="l">{{ l }}</div>
      <span v-if="!log.length" style="color:#9ca3af">Interact with the popover to see events...</span>
    </div>
  `
};

Attributes

Name Type Default Description
trigger-id External trigger element id.
placement Panel placement. Default: bottom.
offset Gap px. Default: 8.
header Plain-text header (alternative to slot="header").
show-close-button Render ✕ button in the header row.
close-on-escape Default: true.
close-on-outside-click Default: true.
arrow Show placement arrow. Default: false.

Properties

Name Type Default Description
triggerId string | undefined
placement Placement 'bottom'
offset number 8
header string | undefined
showCloseButton boolean false
closeOnEscape boolean true
closeOnOutsideClick boolean true
arrow boolean false
isOpen boolean
styles array [styles]

Slots

Name Description
trigger Element that opens the popover on click.
header Optional header area (above content divider).
content Main body content.
footer Optional footer area (below content divider).

Events

Name Type Description
uwc-show CustomEvent
uwc-hide CustomEvent
uwc-close CustomEvent Fired when user clicks the close button.

CSS Custom Properties

Name Default Description
--uwc-popover-bg
--uwc-popover-border
--uwc-popover-radius
--uwc-popover-shadow
--uwc-popover-z
--uwc-popover-duration
--uwc-popover-header-bg
--uwc-popover-header-color

CSS Parts

Name Description
panel
header
body
footer
close-btn
arrow