Dialog
Janela modal sobre a página para tarefas curtas que exigem atenção, como editar um cadastro ou ver detalhes de um pedido.
'use client';
import { Button } from '@/components/ui/button';
import {
Dialog,
DialogClose,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog';
import { TextField } from '@/components/ui/input';
export default function DialogDemo() {
return (
<Dialog>
<Button variant="outline">Editar cliente</Button>
<DialogContent>
<DialogHeader>
<DialogTitle>Editar cliente</DialogTitle>
<DialogDescription>
Atualize os dados de contato da Padaria Pão Quente. Clique em salvar ao terminar.
</DialogDescription>
</DialogHeader>
<div className="grid gap-4">
<TextField label="Nome fantasia" defaultValue="Padaria Pão Quente" />
<TextField label="WhatsApp" type="tel" defaultValue="(11) 98765-4321" />
</div>
<DialogFooter>
<DialogClose>Cancelar</DialogClose>
<Button slot="close">Salvar alterações</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
}Instalação
npx shadcn@latest add @lenstech/dialogUso
import { Button } from "@/components/ui/button"
import {
Dialog,
DialogClose,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog"<Dialog>
<Button variant="outline">Editar cliente</Button>
<DialogContent>
<DialogHeader>
<DialogTitle>Editar cliente</DialogTitle>
<DialogDescription>Atualize os dados de contato.</DialogDescription>
</DialogHeader>
{/* conteúdo */}
<DialogFooter>
<DialogClose>Cancelar</DialogClose>
<Button slot="close">Salvar</Button>
</DialogFooter>
</DialogContent>
</Dialog>Sem DialogTrigger: o gatilho é o primeiro filho
Dialog é o DialogTrigger do React Aria. Não existe um componente DialogTrigger separado como no
shadcn: o primeiro filho (qualquer botão pressionável) abre o diálogo e o segundo é o <DialogContent>.
Qualquer botão com slot="close" — como o DialogClose — fecha o diálogo automaticamente.
O diálogo já cuida da acessibilidade: prende o foco dentro da janela, fecha com Esc, bloqueia a rolagem da página e
devolve o foco ao gatilho ao fechar. DialogTitle rotula o diálogo para leitores de tela — sempre inclua um.
Exemplos
Com formulário
DialogContent aceita uma função como filho, que recebe close. Use-a para fechar o diálogo depois de um envio válido.
'use client';
import { useState } from 'react';
import { Button } from '@/components/ui/button';
import {
Dialog,
DialogClose,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog';
import { Form } from '@/components/ui/form';
import { TextField } from '@/components/ui/input';
export default function DialogForm() {
const [ultimo, setUltimo] = useState<string | null>(null);
return (
<div className="flex flex-col items-center gap-3">
<Dialog>
<Button>Agendar consulta</Button>
<DialogContent>
{({ close }) => (
<Form
onSubmit={(e) => {
e.preventDefault();
const data = new FormData(e.currentTarget);
setUltimo(String(data.get('paciente')));
close();
}}
>
<DialogHeader>
<DialogTitle>Nova consulta</DialogTitle>
<DialogDescription>Clínica Sorriso Leve — preencha os dados do paciente.</DialogDescription>
</DialogHeader>
<TextField name="paciente" label="Nome do paciente" isRequired autoFocus />
<TextField name="telefone" label="Telefone" type="tel" isRequired placeholder="(11) 91234-5678" />
<TextField name="obs" label="Observações" multiline rows={3} />
<DialogFooter>
<DialogClose>Cancelar</DialogClose>
<Button type="submit">Agendar</Button>
</DialogFooter>
</Form>
)}
</DialogContent>
</Dialog>
{ultimo && <p className="text-sm text-muted-foreground">Consulta agendada para {ultimo}.</p>}
</div>
);
}Tamanhos
Use size (sm, md, lg, xl ou full). O padrão é md.
'use client';
import { Button } from '@/components/ui/button';
import {
Dialog,
DialogClose,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog';
const sizes = ['sm', 'md', 'lg', 'xl', 'full'] as const;
export default function DialogSizes() {
return (
<div className="flex flex-wrap items-center justify-center gap-3">
{sizes.map((size) => (
<Dialog key={size}>
<Button variant="outline">{size}</Button>
<DialogContent size={size}>
<DialogHeader>
<DialogTitle>Pedido #1042</DialogTitle>
<DialogDescription>Diálogo no tamanho “{size}”.</DialogDescription>
</DialogHeader>
<p className="text-sm">2× Bolo de cenoura com cobertura · R$ 78,00 — retirada amanhã às 9h.</p>
<DialogFooter>
<DialogClose>Fechar</DialogClose>
</DialogFooter>
</DialogContent>
</Dialog>
))}
</div>
);
}Controlado
Passe isOpen e onOpenChange para Dialog quando precisar abrir o diálogo por código (após salvar, ao clicar numa
linha de tabela etc.). Nesse caso não é preciso um gatilho como filho.
Aberto: não
'use client';
import { useState } from 'react';
import { Button } from '@/components/ui/button';
import {
Dialog,
DialogClose,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog';
export default function DialogControlled() {
const [open, setOpen] = useState(false);
return (
<div className="flex flex-col items-center gap-3">
<Button onPress={() => setOpen(true)}>Ver detalhes do pedido</Button>
<p className="text-sm text-muted-foreground">Aberto: {open ? 'sim' : 'não'}</p>
<Dialog isOpen={open} onOpenChange={setOpen}>
<DialogContent size="sm">
<DialogHeader>
<DialogTitle>Pedido #2087</DialogTitle>
<DialogDescription>Ótica Visão Clara · entregue em 12/09.</DialogDescription>
</DialogHeader>
<ul className="grid gap-1 text-sm">
<li>1× Armação acetato preta — R$ 320,00</li>
<li>2× Lentes antirreflexo — R$ 480,00</li>
</ul>
<DialogFooter>
<DialogClose>Fechar</DialogClose>
</DialogFooter>
</DialogContent>
</Dialog>
</div>
);
}Conteúdo longo
Quando o conteúdo passa da altura da tela, o próprio diálogo ganha rolagem interna.
'use client';
import { Button } from '@/components/ui/button';
import {
Dialog,
DialogClose,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog';
const clausulas = [
'A contratada realiza atualizações de segurança mensais no site.',
'Backups são feitos diariamente e mantidos por 30 dias.',
'O suporte funciona em horário comercial por e-mail e WhatsApp.',
'Alterações de conteúdo simples estão incluídas no plano (até 4 por mês).',
'Novas páginas ou funcionalidades são orçadas à parte.',
'O domínio e a hospedagem permanecem em nome do cliente.',
'O monitoramento de disponibilidade envia alertas em caso de queda.',
'O plano pode ser cancelado com 30 dias de antecedência.',
'Relatórios de acesso são enviados todo início de mês.',
'Casos de força maior suspendem temporariamente os prazos de atendimento.',
'Os dados de clientes são tratados conforme a LGPD.',
'Este contrato é renovado automaticamente a cada 12 meses.',
];
export default function DialogScroll() {
return (
<Dialog>
<Button variant="outline">Ler termos do plano</Button>
<DialogContent size="lg">
<DialogHeader>
<DialogTitle>Termos do plano de manutenção</DialogTitle>
<DialogDescription>Leia com atenção antes de contratar.</DialogDescription>
</DialogHeader>
<ol className="grid list-decimal gap-3 pl-5 text-sm">
{[...clausulas, ...clausulas].map((texto, i) => (
<li key={i}>{texto}</li>
))}
</ol>
<DialogFooter>
<DialogClose>Recusar</DialogClose>
<Button slot="close">Aceitar</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
}Quando usar
- Use para tarefas curtas e focadas: editar um cadastro, ver os detalhes de um pedido, um formulário rápido.
- Não use para confirmar ações destrutivas — use
Alert Dialog. - Não use para avisos passageiros ("Salvo com sucesso") — use
Toast. - Para filtros, menu mobile ou edição mantendo a página visível ao lado, prefira
Sheet. - Para conteúdo pequeno ancorado a um botão, prefira
Popover. - Fluxos longos (várias etapas, muitos campos) ficam melhores em uma página própria.
API
Dialog
É o DialogTrigger do React Aria. Props principais:
Prop
Type
DialogContent
Aceita todas as props do Dialog do React Aria, mais:
Prop
Type
Demais partes
Prop
Type