Popover
Conteúdo flutuante ancorado a um botão, para detalhes, pequenos formulários e explicações que não cabem num tooltip.
'use client';
import { Button } from '@/components/ui/button';
import { TextField } from '@/components/ui/input';
import { PopoverContent, PopoverTrigger } from '@/components/ui/popover';
export default function PopoverDemo() {
return (
<PopoverTrigger>
<Button variant="outline">Horário de funcionamento</Button>
<PopoverContent aria-label="Horário de funcionamento">
<div className="grid gap-4">
<div className="grid gap-1">
<h4 className="font-heading leading-none font-medium">Horário de funcionamento</h4>
<p className="text-sm text-muted-foreground">Exibido no rodapé do site e no Google.</p>
</div>
<div className="grid gap-3">
<TextField label="Segunda a sexta" defaultValue="08:00 – 18:00" />
<TextField label="Sábado" defaultValue="08:00 – 12:00" />
</div>
</div>
</PopoverContent>
</PopoverTrigger>
);
}Instalação
npx shadcn@latest add @lenstech/popoverUso
import { Button } from "@/components/ui/button"
import { PopoverContent, PopoverTrigger } from "@/components/ui/popover"<PopoverTrigger>
<Button variant="outline">Detalhes</Button>
<PopoverContent aria-label="Detalhes">
Conteúdo do popover.
</PopoverContent>
</PopoverTrigger>PopoverContent x Popover
Use PopoverContent para conteúdo livre: ele envolve o conteúdo num Dialog acessível (foco gerenciado,
Esc fecha). Informe aria-label ou um título com slot="title". Popover é só a superfície
posicionada, usada internamente por Menu, Select e ComboBox — raramente você vai usá-lo direto.
Exemplos
Posicionamento
Use placement (top, bottom, left, right e combinações como "bottom start"). Se não houver espaço, o
popover inverte o lado automaticamente.
'use client';
import { Button } from '@/components/ui/button';
import { PopoverContent, PopoverTrigger } from '@/components/ui/popover';
const placements = [
{ placement: 'top', label: 'Acima' },
{ placement: 'bottom', label: 'Abaixo' },
{ placement: 'left', label: 'Esquerda' },
{ placement: 'right', label: 'Direita' },
] as const;
export default function PopoverPlacements() {
return (
<div className="flex flex-wrap items-center justify-center gap-3">
{placements.map(({ placement, label }) => (
<PopoverTrigger key={placement}>
<Button variant="outline">{label}</Button>
<PopoverContent placement={placement} popoverClassName="w-56" aria-label={label}>
<p className="text-sm">
Popover com <code className="font-mono">placement="{placement}"</code>.
</p>
</PopoverContent>
</PopoverTrigger>
))}
</div>
);
}Com seta
showArrow adiciona uma seta apontando para o gatilho — útil quando o botão é pequeno, como um ícone de ajuda.
'use client';
import { Info } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { PopoverContent, PopoverTrigger } from '@/components/ui/popover';
export default function PopoverArrow() {
return (
<div className="flex items-center gap-2 text-sm">
<span>Taxa de entrega: R$ 8,00</span>
<PopoverTrigger>
<Button variant="ghost" size="icon-sm" aria-label="Como a taxa é calculada">
<Info />
</Button>
<PopoverContent showArrow placement="top" aria-label="Como a taxa é calculada">
<div className="grid gap-2 text-sm">
<p className="font-medium">Como calculamos</p>
<p className="text-muted-foreground">
Até 3 km: R$ 8,00. Acima disso, R$ 2,00 por km adicional. Pedidos acima de R$ 120,00 têm entrega
grátis.
</p>
</div>
</PopoverContent>
</PopoverTrigger>
</div>
);
}Com formulário
Como no Dialog, o filho pode ser uma função que recebe close. Botões com slot="close" também fecham o popover.
'use client';
import { useState } from 'react';
import { Button } from '@/components/ui/button';
import { Form } from '@/components/ui/form';
import { TextField } from '@/components/ui/input';
import { PopoverContent, PopoverTrigger } from '@/components/ui/popover';
export default function PopoverForm() {
const [nota, setNota] = useState('');
return (
<div className="flex flex-col items-center gap-3">
<PopoverTrigger>
<Button variant="outline">Adicionar nota</Button>
<PopoverContent aria-label="Adicionar nota ao pedido">
{({ close }) => (
<Form
onSubmit={(e) => {
e.preventDefault();
setNota(String(new FormData(e.currentTarget).get('nota')));
close();
}}
>
<TextField name="nota" label="Nota interna" multiline rows={3} isRequired autoFocus />
<div className="flex justify-end gap-2">
<Button slot="close" variant="ghost" size="sm">
Cancelar
</Button>
<Button type="submit" size="sm">
Salvar
</Button>
</div>
</Form>
)}
</PopoverContent>
</PopoverTrigger>
{nota && <p className="text-sm text-muted-foreground">Nota: {nota}</p>}
</div>
);
}Quando usar
- Use para conteúdo curto e interativo ligado a um elemento: explicar uma taxa, editar um campo, filtros simples.
- Não use para dicas de uma linha sem interação — use
Tooltip. - Não use para uma lista de ações — use
Menu. - Para formulários maiores ou conteúdo que precisa de foco total, use
DialogouSheet.
API
PopoverTrigger
É o DialogTrigger do React Aria: o primeiro filho é o gatilho e o segundo é
o PopoverContent. Aceita isOpen, defaultOpen e onOpenChange.
PopoverContent
Aceita todas as props do Dialog do React Aria, mais:
Prop
Type
Popover
Superfície posicionada sem diálogo. Aceita todas as props do Popover do React Aria
(placement, offset, crossOffset, triggerRef, isOpen…), mais:
Prop
Type