Popover

Popover is a container component that can overlay other components on page.

basic-demo

Usage#

import { Popover } from '@primereact/ui/popover';
<Popover.Root>
    <Popover.Trigger></Popover.Trigger>
    <Popover.Portal>
        <Popover.Positioner sideOffset={12} side="bottom" align="start">
            <Popover.Popup>
                <Popover.Content></Popover.Content>
                <Popover.Close></Popover.Close>
                <Popover.Arrow />
            </Popover.Popup>
        </Popover.Positioner>
    </Popover.Portal>
</Popover.Root>

Examples#

Controlled#

Use the open and onOpenChange props to control the popover state.

controlled-demo
'use client';
import { Times } from '@primeicons/react/times';
import { usePopoverOpenChangeEvent } from '@primereact/types/shared/popover';
import { Button } from '@primereact/ui/button';
import { InputText } from '@primereact/ui/inputtext';
import { Popover } from '@primereact/ui/popover';
import React from 'react';

export default function ControlledDemo() {
    const [open, setOpen] = React.useState(false);

    return (
        <div className="flex gap-4 justify-center items-center">
            <Button onClick={() => setOpen(!open)}>Show Popover</Button>

            <Popover.Root open={open} onOpenChange={(e: usePopoverOpenChangeEvent) => setOpen(e.value)}>
                <Popover.Trigger>Popover Trigger</Popover.Trigger>
                <Popover.Portal>
                    <Popover.Positioner sideOffset={12}>
                        <Popover.Popup className="max-w-72 w-full">
                            <Popover.Arrow />
                            <Popover.Header>
                                <Popover.Title>Create a New Workspace</Popover.Title>
                                <Popover.Close as={Button} severity="secondary" variant="text" size="small" iconOnly>
                                    <Times />
                                </Popover.Close>
                            </Popover.Header>
                            <Popover.Content>
                                <Popover.Description>Name your workspace to get started. You can always change this later.</Popover.Description>
                                <InputText placeholder="Workspace Name" className="mt-3 w-full" />
                            </Popover.Content>
                            <Popover.Footer>
                                <span className="text-xs text-surface-500 dark:text-surface-400 ">1 of 3</span>
                                <div className="flex-1 flex items-center justify-end gap-2">
                                    <Button severity="secondary" variant="outlined" size="small">
                                        Back
                                    </Button>
                                    <Button size="small">Next</Button>
                                </div>
                            </Popover.Footer>
                        </Popover.Popup>
                    </Popover.Positioner>
                </Popover.Portal>
            </Popover.Root>
        </div>
    );
}

Alignment#

Use the side and align props to align the popover. To give an offset to the popover, use the sideOffset and alignOffset props.

alignment-demo
'use client';
import { Times } from '@primeicons/react/times';
import { Button } from '@primereact/ui/button';
import { InputText } from '@primereact/ui/inputtext';
import { Popover } from '@primereact/ui/popover';
import * as React from 'react';

const sides = ['top', 'right', 'bottom', 'left'] as const;
const aligns = ['start', 'center', 'end'] as const;

export default function AlignmentDemo() {
    const [side, setSide] = React.useState<(typeof sides)[number]>('bottom');
    const [align, setAlign] = React.useState<(typeof aligns)[number]>('start');

    return (
        <div className="flex flex-col items-center min-h-60">
            <div className="flex flex-col items-center justify-center gap-4 **:capitalize">
                <div className="flex items-center justify-center gap-4 ">
                    {sides.map((s: (typeof sides)[number]) => (
                        <Button
                            key={s}
                            severity={side === s ? 'primary' : 'secondary'}
                            variant={side === s ? undefined : 'outlined'}
                            size="small"
                            onClick={() => setSide(s)}
                        >
                            {s}
                        </Button>
                    ))}
                </div>
                <div className="flex items-center justify-center gap-4">
                    {aligns.map((a: (typeof aligns)[number]) => (
                        <Button
                            key={a}
                            severity={align === a ? 'primary' : 'secondary'}
                            variant={align === a ? undefined : 'outlined'}
                            size="small"
                            onClick={() => setAlign(a)}
                        >
                            {a}
                        </Button>
                    ))}
                </div>
            </div>
            <Popover.Root open>
                <Popover.Trigger className="my-60 px-2.5 h-8 rounded-lg border border-surface flex items-center justify-center gap-2 bg-surface-0 dark:bg-surface-950 hover:bg-surface-100 dark:hover:bg-surface-900 data-open:bg-surface-100 dark:data-open:bg-surface-900 transition-colors text-sm font-medium">
                    Show Popover
                </Popover.Trigger>
                <Popover.Portal>
                    <Popover.Positioner sideOffset={12} side={side} align={align}>
                        <Popover.Popup className="max-w-72 w-full">
                            <Popover.Arrow />
                            <Popover.Header>
                                <Popover.Title>Create a New Workspace</Popover.Title>
                                <Popover.Close as={Button} severity="secondary" variant="text" size="small" iconOnly>
                                    <Times />
                                </Popover.Close>
                            </Popover.Header>
                            <Popover.Content>
                                <Popover.Description>Name your workspace to get started. You can always change this later.</Popover.Description>
                                <InputText placeholder="Workspace Name" className="mt-3 w-full" />
                            </Popover.Content>
                            <Popover.Footer>
                                <span className="text-xs text-surface-500 dark:text-surface-400 ">1 of 3</span>
                                <div className="flex-1 flex items-center justify-end gap-2">
                                    <Button severity="secondary" variant="outlined" size="small">
                                        Back
                                    </Button>
                                    <Button size="small">Next</Button>
                                </div>
                            </Popover.Footer>
                        </Popover.Popup>
                    </Popover.Positioner>
                </Popover.Portal>
            </Popover.Root>
        </div>
    );
}

Accessibility#

Screen Reader#

Popover component uses dialog role and since any attribute is passed to the root element, attributes like aria-label or aria-labelledby can be defined to describe the popup contents. In addition aria-modal is added since focus is kept within the popup.

Popover adds aria-expanded state attribute and aria-controls to the trigger so that the relation between the trigger and the popup is defined.

Popover Keyboard Support#

When the popup gets opened, the first focusable element receives the focus and this can be customized by adding autofocus to an element within the popup.

KeyFunction
tabMoves focus to the next the focusable element within the popup.
shift + tabMoves focus to the previous the focusable element within the popup.
escapeCloses the popup and moves focus to the trigger.