Saltar al contenido principal

Accordion

Alternar secciones de contenido. Mantener layouts limpios. Enfocar atención.

Every teammate you invite takes a seat. New seats are prorated for the days left in the billing period, and seats you remove come back as credit on the next invoice.
1'use client';
2
3import { Accordion } from '@/components/ui/accordion';
4
5const faq = [
6 {
7 value: 'seats',
8 question: 'How are seats billed?',
9 answer:
10 'Every teammate you invite takes a seat. New seats are prorated for the days left in the billing period, and seats you remove come back as credit on the next invoice.',
11 },
12 {
13 value: 'plan-change',
14 question: 'Can I switch plans in the middle of a cycle?',
15 answer:
16 'Yes. Upgrades apply right away and you pay the difference for the rest of the month. Downgrades take effect on the renewal date so you keep what you already paid for.',
17 },
18 {
19 value: 'invoices',
20 question: 'Where do I find past invoices?',
21 answer:
22 'Open Settings, then Billing. Every invoice is there as a PDF, and the billing contact gets a copy by email within a few minutes of each charge.',
23 },
24 {
25 value: 'overage',
26 question: 'What happens if we go over the included usage?',
27 answer:
28 'We keep serving traffic and add the extra usage to your next invoice at the rate listed on your plan. You get an email as soon as you pass 80% of the included quota.',
29 },
30];
31
32export function Default() {
33 return (
34 <Accordion type="single" className="w-full max-w-md" defaultValue="seats">
35 {faq.map((item) => (
36 <Accordion.Item key={item.value} value={item.value}>
37 <Accordion.Trigger value={item.value}>{item.question}</Accordion.Trigger>
38 <Accordion.Content value={item.value} className="text-muted-foreground text-sm">
39 {item.answer}
40 </Accordion.Content>
41 </Accordion.Item>
42 ))}
43 </Accordion>
44 );
45}

Instalación

pnpm dlx nachui add accordion

Anatomía

1import { Accordion } from '@/components/ui/accordion';
1<Accordion type="single" defaultValue="item-1">
2 <Accordion.Item value="item-1">
3 <Accordion.Trigger>¿Es accesible?</Accordion.Trigger>
4 <Accordion.Content>Sí. Se adhiere al estándar de diseño WAI-ARIA.</Accordion.Content>
5 </Accordion.Item>
6</Accordion>

Composición

Usa la siguiente composición para construir un Accordion:
Accordion
└── Accordion.Item
├── Accordion.Trigger
└── Accordion.Content

Variantes

Single

El tipo single permite que solo un ítem esté abierto a la vez.
Every teammate you invite takes a seat. New seats are prorated for the days left in the billing period, and seats you remove come back as credit on the next invoice.
1'use client';
2
3import { Accordion } from '@/components/ui/accordion';
4
5const faq = [
6 {
7 value: 'seats',
8 question: 'How are seats billed?',
9 answer:
10 'Every teammate you invite takes a seat. New seats are prorated for the days left in the billing period, and seats you remove come back as credit on the next invoice.',
11 },
12 {
13 value: 'plan-change',
14 question: 'Can I switch plans in the middle of a cycle?',
15 answer:
16 'Yes. Upgrades apply right away and you pay the difference for the rest of the month. Downgrades take effect on the renewal date so you keep what you already paid for.',
17 },
18 {
19 value: 'invoices',
20 question: 'Where do I find past invoices?',
21 answer:
22 'Open Settings, then Billing. Every invoice is there as a PDF, and the billing contact gets a copy by email within a few minutes of each charge.',
23 },
24 {
25 value: 'overage',
26 question: 'What happens if we go over the included usage?',
27 answer:
28 'We keep serving traffic and add the extra usage to your next invoice at the rate listed on your plan. You get an email as soon as you pass 80% of the included quota.',
29 },
30];
31
32export function Default() {
33 return (
34 <Accordion type="single" className="w-full max-w-md" defaultValue="seats">
35 {faq.map((item) => (
36 <Accordion.Item key={item.value} value={item.value}>
37 <Accordion.Trigger value={item.value}>{item.question}</Accordion.Trigger>
38 <Accordion.Content value={item.value} className="text-muted-foreground text-sm">
39 {item.answer}
40 </Accordion.Content>
41 </Accordion.Item>
42 ))}
43 </Accordion>
44 );
45}

Multiple

El tipo multiple permite que múltiples ítems estén abiertos simultáneamente.
1'use client';
2
3import { Accordion } from '@/components/ui/accordion';
4
5const causes = [
6 {
7 value: 'module-not-found',
8 trigger: 'Module not found during the build step',
9 content:
10 'The package resolves locally but not on the build machine. Move it out of devDependencies, commit the lockfile, and rerun the build with a cleared cache.',
11 },
12 {
13 value: 'out-of-memory',
14 trigger: 'Build ran out of memory',
15 content:
16 'Node stops at the default heap limit on large bundles. Set NODE_OPTIONS to --max-old-space-size=4096 in the project settings, then split the largest entry point if it happens again.',
17 },
18 {
19 value: 'missing-env',
20 trigger: 'Environment variable was undefined at build time',
21 content:
22 'Variables scoped to Preview are not injected into Production builds. Check the scope column in Settings, Environment Variables, and remember that only NEXT_PUBLIC_ values reach the browser bundle.',
23 },
24];
25
26export function Multiple() {
27 return (
28 <Accordion type="multiple" className="w-full max-w-md">
29 {causes.map((cause) => (
30 <Accordion.Item key={cause.value} value={cause.value}>
31 <Accordion.Trigger value={cause.value}>{cause.trigger}</Accordion.Trigger>
32 <Accordion.Content value={cause.value} className="text-muted-foreground text-sm">
33 {cause.content}
34 </Accordion.Content>
35 </Accordion.Item>
36 ))}
37 </Accordion>
38 );
39}

Sin valor por defecto

Puedes omitir defaultValue para que el acordeón inicie completamente cerrado.
1'use client';
2
3import { Accordion } from '@/components/ui/accordion';
4
5const sections = [
6 {
7 value: 'authentication',
8 trigger: 'Authentication',
9 content:
10 'Send your secret key as a bearer token on every request. Keys are scoped per project, so a test key never touches live orders.',
11 },
12 {
13 value: 'rate-limits',
14 trigger: 'Rate limits',
15 content:
16 'The API allows 600 requests per minute per key. Every response carries the remaining budget in X-RateLimit-Remaining, and a 429 tells you how long to wait.',
17 },
18 {
19 value: 'webhooks',
20 trigger: 'Webhook retries',
21 content:
22 'A webhook that does not return a 2xx within 5 seconds is retried 8 times over 24 hours with exponential backoff. Reply fast and queue the work on your side.',
23 },
24 {
25 value: 'versioning',
26 trigger: 'Versioning',
27 content:
28 'Your account is pinned to the API version that was current when you signed up. Breaking changes ship under a new date version and never move you off yours.',
29 },
30];
31
32export function Collapsed() {
33 return (
34 <Accordion type="single" className="w-full max-w-md">
35 {sections.map((section) => (
36 <Accordion.Item key={section.value} value={section.value}>
37 <Accordion.Trigger value={section.value}>{section.trigger}</Accordion.Trigger>
38 <Accordion.Content value={section.value} className="text-muted-foreground text-sm">
39 {section.content}
40 </Accordion.Content>
41 </Accordion.Item>
42 ))}
43 </Accordion>
44 );
45}

Referencia de API

Accordion

PropTipoDefaultDescripción
type"single" | "multiple""single"Controla si se puede abrir un solo ítem o varios simultáneamente.
defaultValuestringundefinedÍtem inicial abierto.
childrenReact.ReactNodeDeben ser AccordionItem.
classNamestringClases adicionales para el contenedor.

Accordion.Item

PropTipoDefaultDescripción
valuestringID único del ítem. Obligatorio.
childrenReact.ReactNodeDebe incluir AccordionTrigger y AccordionContent.
isOpenbooleanControlado internoIndica si el ítem está abierto.
onToggle() => voidControlado internoFunción para abrir/cerrar.
classNamestringClases adicionales para el wrapper.

Accordion.Trigger

PropTipoDefaultDescripción
childrenReact.ReactNodeTítulo o contenido del botón.
isOpenbooleanControlado internoEstado abierto/cerrado (rota el ícono).
onToggle() => voidControlado internoAcción al hacer click.
classNamestringClases adicionales del botón.

Accordion.Content

PropTipoDefaultDescripción
childrenReact.ReactNodeContenido colapsable.
isOpenbooleanControlado internoControla apertura y animación.
classNamestringClases adicionales para el contenedor animado.
¿Encontraste algo que mejorar?

¿Notaste un error, tipografía o detalle faltante en esta página? Ayúdanos a mejorar la documentación abriendo un issue en GitHub.

Crear un Issue
Ctrl+I