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>
`
};
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>
`
};
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>
`
};
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>
`
};
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 |
— |