Accordion
Lista de seções que expandem e recolhem, ideal para perguntas frequentes e detalhes de serviços.
'use client';
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/components/ui/accordion';
const perguntas = [
{
id: 'convenios',
pergunta: 'Vocês atendem por convênio?',
resposta: 'Sim. Atendemos Amil Dental, Bradesco Dental, OdontoPrev e SulAmérica. Traga a carteirinha na primeira consulta.',
},
{
id: 'horarios',
pergunta: 'Qual o horário de funcionamento?',
resposta: 'Segunda a sexta, das 8h às 19h, e sábado das 8h às 13h.',
},
{
id: 'pagamento',
pergunta: 'Quais formas de pagamento são aceitas?',
resposta: 'Pix, cartão de débito e crédito em até 10x sem juros para tratamentos acima de R$ 500.',
},
];
export default function AccordionDemo() {
return (
<Accordion className="max-w-lg">
{perguntas.map((item) => (
<AccordionItem key={item.id} id={item.id}>
<AccordionTrigger>{item.pergunta}</AccordionTrigger>
<AccordionContent className="text-muted-foreground">{item.resposta}</AccordionContent>
</AccordionItem>
))}
</Accordion>
);
}Instalação
npx shadcn@latest add @lenstech/accordionUso
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion"<Accordion>
<AccordionItem id="horarios">
<AccordionTrigger>Qual o horário de funcionamento?</AccordionTrigger>
<AccordionContent>Segunda a sexta, das 8h às 19h.</AccordionContent>
</AccordionItem>
</Accordion>Exemplos
Várias seções abertas
Por padrão só uma seção fica aberta por vez. Use allowsMultipleExpanded para permitir várias e defaultExpandedKeys para definir quais começam abertas.
'use client';
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/components/ui/accordion';
export default function AccordionMultiple() {
return (
<Accordion allowsMultipleExpanded defaultExpandedKeys={['abertura', 'mensal']} className="max-w-lg">
<AccordionItem id="abertura">
<AccordionTrigger>Abertura de empresa</AccordionTrigger>
<AccordionContent className="text-muted-foreground">
Registro na Junta Comercial, CNPJ, inscrição municipal e alvará em até 15 dias úteis.
</AccordionContent>
</AccordionItem>
<AccordionItem id="mensal">
<AccordionTrigger>Contabilidade mensal</AccordionTrigger>
<AccordionContent className="text-muted-foreground">
Apuração de impostos, folha de pagamento e envio das obrigações acessórias.
</AccordionContent>
</AccordionItem>
<AccordionItem id="irpf">
<AccordionTrigger>Declaração de Imposto de Renda</AccordionTrigger>
<AccordionContent className="text-muted-foreground">
IRPF para sócios e pessoas físicas, com análise de deduções.
</AccordionContent>
</AccordionItem>
</Accordion>
);
}Item desabilitado
'use client';
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/components/ui/accordion';
export default function AccordionDisabled() {
return (
<Accordion defaultExpandedKeys={['entrega']} className="max-w-lg">
<AccordionItem id="entrega">
<AccordionTrigger>Entrega</AccordionTrigger>
<AccordionContent className="text-muted-foreground">
Enviamos para todo o Brasil. Frete grátis em compras acima de R$ 199.
</AccordionContent>
</AccordionItem>
<AccordionItem id="trocas">
<AccordionTrigger>Trocas e devoluções</AccordionTrigger>
<AccordionContent className="text-muted-foreground">
Você tem até 7 dias após o recebimento para solicitar a troca.
</AccordionContent>
</AccordionItem>
<AccordionItem id="retirada" isDisabled>
<AccordionTrigger>Retirada na loja (em breve)</AccordionTrigger>
<AccordionContent className="text-muted-foreground">Disponível a partir de novembro.</AccordionContent>
</AccordionItem>
</Accordion>
);
}Controlado
Com expandedKeys + onExpandedChange você abre e fecha seções a partir do seu código. Os valores são os ids dos AccordionItem.
'use client';
import { useState } from 'react';
import type { Key } from 'react-aria-components';
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/components/ui/accordion';
import { Button } from '@/components/ui/button';
const etapas = [
{ id: 'dados', titulo: '1. Dados do responsável', texto: 'Nome, CPF e telefone de quem vai acompanhar o paciente.' },
{ id: 'saude', titulo: '2. Histórico de saúde', texto: 'Alergias, medicamentos em uso e cirurgias anteriores.' },
{ id: 'termos', titulo: '3. Termo de consentimento', texto: 'Leia e aceite o termo antes da primeira consulta.' },
];
export default function AccordionControlled() {
const [abertos, setAbertos] = useState<Set<Key>>(new Set(['dados']));
return (
<div className="flex w-full max-w-lg flex-col gap-4">
<Accordion expandedKeys={abertos} onExpandedChange={setAbertos}>
{etapas.map((etapa) => (
<AccordionItem key={etapa.id} id={etapa.id}>
<AccordionTrigger>{etapa.titulo}</AccordionTrigger>
<AccordionContent className="text-muted-foreground">{etapa.texto}</AccordionContent>
</AccordionItem>
))}
</Accordion>
<div className="flex gap-2">
<Button size="sm" variant="outline" onPress={() => setAbertos(new Set(['termos']))}>
Abrir termo
</Button>
<Button size="sm" variant="ghost" onPress={() => setAbertos(new Set())}>
Fechar tudo
</Button>
</div>
</div>
);
}Seção única
Um AccordionItem também funciona sozinho, fora do Accordion — é o equivalente ao Disclosure do React Aria.
'use client';
import { AccordionContent, AccordionItem, AccordionTrigger } from '@/components/ui/accordion';
export default function AccordionSingleItem() {
return (
<AccordionItem className="w-full max-w-lg border-b-0">
<AccordionTrigger>Ver detalhes do pedido #10482</AccordionTrigger>
<AccordionContent className="grid gap-1 text-muted-foreground">
<span>2× Café especial 250g — R$ 79,80</span>
<span>1× Granola artesanal 500g — R$ 32,90</span>
<span className="font-medium text-foreground">Total: R$ 112,70</span>
</AccordionContent>
</AccordionItem>
);
}Accordion ou Disclosure?
O Accordion é um DisclosureGroup do React Aria e cada AccordionItem é um Disclosure.
Use o grupo quando as seções são irmãs (FAQ) e o item sozinho para um único "ver mais".
Para controlar, use expandedKeys/defaultExpandedKeys — não existem type="single" nem value como no Radix.
Quando usar
- Use em FAQ, detalhes de planos/serviços e formulários longos divididos em seções.
- Não use para esconder informação essencial — o que o usuário sempre precisa ver deve ficar visível.
- Para alternar conteúdos do mesmo nível sem empilhar, use
Tabs. - Para agrupar conteúdo sempre visível, use
Card.
API
Accordion
Aceita todas as props do DisclosureGroup do React Aria:
Prop
Type
AccordionItem
Aceita todas as props do Disclosure do React Aria:
Prop
Type
AccordionTrigger
Prop
Type
AccordionContent
Prop
Type