Skip to main content

Accordion

Accordions organize content into expandable sections.

Import

import { Accordion } from '@vortexlabs/vortex';
import type { AccordionProps } from '@vortexlabs/vortex';

Other links

Features

  • Supports single or multiple expanded sections.
  • Supports controlled values and initially expanded items.
  • Controls trigger sizes, spacing, and divider lines.
  • Can disable individual items.
  • Supports custom triggers with your own buttons.

Usage

Place a Trigger and Content inside each Item. Give every item a unique value; defaultValue chooses the initially expanded section.

Standard delivery takes three to five business days.
"use client";

import { Accordion, Stack } from "@vortexlabs/vortex";

export const AccordionUsage = () => {
  return (
    <Accordion.Root defaultValue="shipping" divider gap={3}>
      <Accordion.Item value="shipping">
        <Accordion.Trigger paddingBlock={3}>
          When will my order arrive?
        </Accordion.Trigger>
        <Accordion.Content>
          <Stack paddingBottom={4}>
            Standard delivery takes three to five business days.
          </Stack>
        </Accordion.Content>
      </Accordion.Item>
      <Accordion.Item value="returns">
        <Accordion.Trigger paddingBlock={3}>Can I return an item?</Accordion.Trigger>
        <Accordion.Content>
          <Stack paddingBottom={4}>
            You can request a return within 30 days.
          </Stack>
        </Accordion.Content>
      </Accordion.Item>
    </Accordion.Root>
  );
};

Multiple Sections

The default type="single" allows one open item. Set type="multiple" to expand items independently and pass an array to defaultValue.

Delivery is free for orders over $75.
Keep the original packaging for your return.
"use client";

import { Accordion, Stack } from "@vortexlabs/vortex";

export const AccordionMultiple = () => {
  return (
    <Accordion.Root
      type="multiple"
      defaultValue={["delivery", "returns"]}
      divider
      gap={3}
    >
      <Accordion.Item value="delivery">
        <Accordion.Trigger paddingBlock={3}>Delivery</Accordion.Trigger>
        <Accordion.Content>
          <Stack paddingBottom={4}>
            Delivery is free for orders over $75.
          </Stack>
        </Accordion.Content>
      </Accordion.Item>
      <Accordion.Item value="returns">
        <Accordion.Trigger paddingBlock={3}>Returns</Accordion.Trigger>
        <Accordion.Content>
          <Stack paddingBottom={4}>
            Keep the original packaging for your return.
          </Stack>
        </Accordion.Content>
      </Accordion.Item>
    </Accordion.Root>
  );
};

Controlled State

Use value and onChange when another control needs to open or close an item. In single mode, null closes every item; multiple mode uses a string array.

Open section: none
"use client";

import { Accordion, Button, Stack, Text } from "@vortexlabs/vortex";
import { useState } from "react";

export const AccordionControlled = () => {
  const [value, setValue] = useState<string | null>(null);
  return (
    <Stack gap={4}>
      <Stack direction="row" gap={3} wrap>
        <Button size="sm" onClick={() => setValue("details")}>Show details</Button>
        <Button size="sm" variant="secondary" onClick={() => setValue(null)}>
          Close all
        </Button>
      </Stack>
      <Accordion.Root value={value} onChange={setValue} gap={3}>
        <Accordion.Item value="details">
          <Accordion.Trigger>Order details</Accordion.Trigger>
          <Accordion.Content>Your order contains two items.</Accordion.Content>
        </Accordion.Item>
      </Accordion.Root>
      <Text size="body-sm-desktop">Open section: {value ?? "none"}</Text>
    </Stack>
  );
};

Sizes

Trigger size styles the label as md or lg. Set iconSize on the root to change every expand icon.

"use client";

import { Accordion, Stack } from "@vortexlabs/vortex";

export const AccordionSizes = () => {
  return (
    <Stack gap={5}>
      <Accordion.Root iconSize={4}>
        <Accordion.Item value="compact">
          <Accordion.Trigger size="md">Medium label and smaller icon</Accordion.Trigger>
          <Accordion.Content>Compact order information.</Accordion.Content>
        </Accordion.Item>
      </Accordion.Root>
      <Accordion.Root iconSize={{ xs: 5, md: 6 }}>
        <Accordion.Item value="large">
          <Accordion.Trigger size="lg">
            Large label and responsive icon
          </Accordion.Trigger>
          <Accordion.Content>More prominent order information.</Accordion.Content>
        </Accordion.Item>
      </Accordion.Root>
    </Stack>
  );
};

Spacing and Dividers

Use divider to separate items. Trigger padding controls the clickable area; root gap adds space above expanded content. Add bottom padding inside Content to separate its text from the next divider.

Use the address associated with your payment method.
"use client";

import { Accordion, Stack } from "@vortexlabs/vortex";

export const AccordionSpacing = () => {
  return (
    <Accordion.Root divider gap={{ xs: 2, md: 4 }} defaultValue="billing">
      <Accordion.Item value="billing">
        <Accordion.Trigger paddingBlock={{ xs: 3, md: 4 }} paddingInline={2}>
          Billing address
        </Accordion.Trigger>
        <Accordion.Content>
          <Stack paddingInline={2} paddingBottom={4}>
            Use the address associated with your payment method.
          </Stack>
        </Accordion.Content>
      </Accordion.Item>
      <Accordion.Item value="payment">
        <Accordion.Trigger paddingBlock={{ xs: 3, md: 4 }} paddingInline={2}>
          Payment methods
        </Accordion.Trigger>
        <Accordion.Content>
          <Stack paddingInline={2} paddingBottom={4}>
            We accept credit and debit cards.
          </Stack>
        </Accordion.Content>
      </Accordion.Item>
    </Accordion.Root>
  );
};

Disabled Items

Set disabled on an Item to prevent it from being toggled. Other items remain interactive.

"use client";

import { Accordion, Stack } from "@vortexlabs/vortex";

export const AccordionDisabled = () => {
  return (
    <Accordion.Root divider gap={3}>
      <Accordion.Item value="available">
        <Accordion.Trigger paddingBlock={3}>Delivery information</Accordion.Trigger>
        <Accordion.Content>
          <Stack paddingBottom={4}>Your delivery details appear here.</Stack>
        </Accordion.Content>
      </Accordion.Item>
      <Accordion.Item value="unavailable" disabled>
        <Accordion.Trigger paddingBlock={3}>
          Tracking available after dispatch
        </Accordion.Trigger>
        <Accordion.Content>
          <Stack paddingBottom={4}>
            Your tracking number appears after dispatch.
          </Stack>
        </Accordion.Content>
      </Accordion.Item>
    </Accordion.Root>
  );
};

Custom Triggers

Pass a render function to Trigger to use a Vortex Button as the trigger. Pass the supplied attributes through Button.attributes to preserve expansion and the connection to its panel. Forward disabled to keep the button's behavior and styling in sync.

"use client";

import { Accordion, Button } from "@vortexlabs/vortex";

export const AccordionCustom = () => {
  return (
    <Accordion.Root gap={3}>
      <Accordion.Item value="summary">
        <Accordion.Trigger>
          {(attributes, { active, disabled }) => (
            <Button
              type="button"
              size="sm"
              variant="secondary"
              disabled={disabled}
              attributes={attributes}
            >
              {active ? "Hide order summary −" : "Show order summary +"}
            </Button>
          )}
        </Accordion.Trigger>
        <Accordion.Content>Two items · Free delivery · $84 total</Accordion.Content>
      </Accordion.Item>
    </Accordion.Root>
  );
};

Accessibility

Key
Description
EnterSpace
Activate the focused trigger to expand or collapse its item.
TabShift+Tab
Move between enabled triggers and focusable content in expanded panels.
Default triggers are buttons with aria-expanded and aria-controls. Custom triggers must retain the supplied attributes and a meaningful accessible name.