Skip to content

Popover ​

A popup of content anchored to a trigger, as a compound component on Base UI's popover. Use it for something a person reads or fills in; a list of actions is a Menu.

tsx
import { Popover } from '@basmilius/react-ui';
tsx
import { Bell } from 'lucide-react';
import { Button, IconButton, Popover } from '@basmilius/react-ui';

export default function PopoverDemo() {
    return (
        <Popover.Root>
            <IconButton icon={Bell} label="Notifications" render={<Popover.Trigger />} />
            <Popover.Popup className="w-72 p-3">
                <Popover.Title className="text-sm font-medium text-text">Notifications</Popover.Title>
                <Popover.Description className="mt-1 text-xs text-text-muted">Nothing new since you last looked.</Popover.Description>
                <div className="mt-3 flex justify-end">
                    <Popover.Close render={<Button size="sm" variant="secondary" />}>Close</Popover.Close>
                </div>
            </Popover.Popup>
        </Popover.Root>
    );
}

Parts ​

PartWhat it is
Popover.RootHolds the open state: open and onOpenChange, or defaultOpen.
Popover.TriggerThe button that opens it.
Popover.PopupThe portal, the positioner and the popup in one part.
Popover.TitleA heading, which names the popup for a screen reader.
Popover.DescriptionA sentence that describes it.
Popover.CloseA button that closes it.

Variants ​

variant decides the surface. menu, the default, is the padded card menus use. picker is a card whose content runs to its edges, such as a search field on top of a list. plain leaves the surface to you.

tsx
import { useState } from 'react';
import { Button, Input, Popover } from '@basmilius/react-ui';

const PEOPLE = ['Ada Lovelace', 'Alan Turing', 'Grace Hopper', 'Katherine Johnson', 'Margaret Hamilton'];

export default function PopoverPicker() {
    const [query, setQuery] = useState('');
    const matches = PEOPLE.filter((person) => person.toLowerCase().includes(query.toLowerCase()));

    return (
        <Popover.Root>
            <Popover.Trigger render={<Button variant="secondary" />}>Assign</Popover.Trigger>
            <Popover.Popup variant="picker">
                <div className="border-b border-border p-1">
                    <Input
                        size="sm"
                        aria-label="Find a person"
                        placeholder="Find a person"
                        className="border-transparent"
                        value={query}
                        onChange={(event) => setQuery(event.target.value)}
                    />
                </div>
                <ul className="max-h-60 overflow-auto p-1">
                    {matches.map((person) => (
                        <li key={person} className="rounded-md px-2.5 py-1.5 text-sm text-text hover:bg-surface-hover">
                            {person}
                        </li>
                    ))}
                </ul>
            </Popover.Popup>
        </Popover.Root>
    );
}

Placement ​

A popover opens under its trigger, lined up with its start, 8 pixels away. Popover.Popup takes the same placement props as Menu.Popup.

Keyboard ​

Opening moves focus into the popup, Escape closes it, and focus goes back to the trigger.

PopoverPopupProps and PopupVariant are exported types.