# AccordionItem

**📖 Live documentation:** https://cds.coinbase.com/components/layout/AccordionItem/

An individual collapsible item within an Accordion.

## Import

```tsx
import { AccordionItem } from '@coinbase/cds-web/accordion/AccordionItem'
```

## Examples

AccordionItem represents a single expandable section within an [Accordion](/components/layout/Accordion). It composes an `AccordionHeader` (clickable trigger) and `AccordionPanel` (collapsible content) into a single component.

### Basics

Each `AccordionItem` requires a unique `itemKey` to identify it within the parent Accordion, and a `title` for the header text. The `children` become the collapsible content.

```jsx live
<Accordion>
  <AccordionItem itemKey="1" title="What is Coinbase?">
    <Text as="p" font="body" color="fgMuted">
      Coinbase is a secure online platform for buying, selling, transferring, and storing digital
      currency.
    </Text>
  </AccordionItem>
</Accordion>
```

### Header Content

#### Title and Subtitle

Use the `title` prop for the main header text and `subtitle` for secondary information.

```jsx live
<Accordion>
  <AccordionItem
    itemKey="1"
    title="Account Settings"
    subtitle="Manage your profile, security, and preferences"
  >
    <VStack gap={2}>
      <Text as="p" font="body" color="fgMuted">
        Configure your account settings here.
      </Text>
      <Button variant="secondary" compact>
        Edit profile
      </Button>
    </VStack>
  </AccordionItem>
</Accordion>
```

#### Media

Add icons, avatars, or other media to the header using the `media` prop. This commonly uses `CellMedia` for consistent styling.

```jsx live
<Accordion>
  <AccordionItem
    itemKey="1"
    title="Bitcoin"
    subtitle="BTC"
    media={<CellMedia active type="icon" name="wallet" title="BTC" />}
  >
    <VStack gap={2}>
      <HStack justifyContent="space-between">
        <Text font="body" color="fgMuted">
          Balance
        </Text>
        <Text font="body">0.5 BTC</Text>
      </HStack>
      <HStack justifyContent="space-between">
        <Text font="body" color="fgMuted">
          Value
        </Text>
        <Text font="body">$21,500.00</Text>
      </HStack>
    </VStack>
  </AccordionItem>
</Accordion>
```

### Click Handling

Use the `onClick` callback to respond when an item is clicked. It receives the `itemKey` as an argument.

```jsx live
function ClickExample() {
  const [lastClicked, setLastClicked] = useState(null);

  return (
    <VStack gap={3}>
      {lastClicked && (
        <Text font="label2" color="fgMuted">
          Last clicked: {lastClicked}
        </Text>
      )}
      <Accordion>
        <AccordionItem itemKey="first" title="First Item" onClick={(key) => setLastClicked(key)}>
          <Text as="p" font="body" color="fgMuted">
            Content for the first item.
          </Text>
        </AccordionItem>
        <AccordionItem itemKey="second" title="Second Item" onClick={(key) => setLastClicked(key)}>
          <Text as="p" font="body" color="fgMuted">
            Content for the second item.
          </Text>
        </AccordionItem>
      </Accordion>
    </VStack>
  );
}
```

### Panel Content

#### Rich Content

AccordionItem children can contain any React content—forms, lists, buttons, or other components.

```jsx live
<Accordion>
  <AccordionItem itemKey="1" title="Payment Details" subtitle="Enter your payment information">
    <VStack gap={3}>
      <TextInput compact label="Card Number" placeholder="1234 5678 9012 3456" />
      <HStack gap={2}>
        <Box flex={1}>
          <TextInput compact label="Expiry" placeholder="MM/YY" />
        </Box>
        <Box flex={1}>
          <TextInput compact label="CVV" placeholder="123" />
        </Box>
      </HStack>
      <HStack gap={2} justifyContent="flex-end">
        <Button variant="secondary">Cancel</Button>
        <Button>Save Card</Button>
      </HStack>
    </VStack>
  </AccordionItem>
</Accordion>
```

#### Max Height

Use `maxHeight` to limit the panel height for items with potentially long content. The content will scroll if it exceeds this height.

```jsx live
<Accordion defaultActiveKey="1">
  <AccordionItem itemKey="1" title="Terms and Conditions" maxHeight={150}>
    <VStack gap={2}>
      <Text as="p" font="body" color="fgMuted">
        Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut
        labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco
        laboris nisi ut aliquip ex ea commodo consequat.
      </Text>
      <Text as="p" font="body" color="fgMuted">
        Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
        pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt
        mollit anim id est laborum.
      </Text>
      <Text as="p" font="body" color="fgMuted">
        Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque
        laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi
        architecto beatae vitae dicta sunt explicabo.
      </Text>
    </VStack>
  </AccordionItem>
</Accordion>
```

### Multiple Items

When using multiple AccordionItems, only one can be expanded at a time (controlled by the parent Accordion). Use `defaultActiveKey` on the Accordion to specify which item starts expanded.

```jsx live
<Accordion defaultActiveKey="faq-2">
  <AccordionItem itemKey="faq-1" title="How do I get started?">
    <Text as="p" font="body" color="fgMuted">
      Download the app, create an account, and verify your identity to start trading.
    </Text>
  </AccordionItem>
  <AccordionItem itemKey="faq-2" title="Is my crypto secure?">
    <Text as="p" font="body" color="fgMuted">
      Yes, we use industry-leading security measures including cold storage and two-factor
      authentication to protect your assets.
    </Text>
  </AccordionItem>
  <AccordionItem itemKey="faq-3" title="What are the fees?">
    <Text as="p" font="body" color="fgMuted">
      Fees vary based on your payment method and transaction size. See our pricing page for details.
    </Text>
  </AccordionItem>
</Accordion>
```

### Accessibility

AccordionItem automatically provides accessible behavior:

- The header uses proper ARIA attributes (`aria-expanded`, `aria-controls`)
- Content is wrapped in a `region` role with `aria-labelledby` pointing to the header
- Full keyboard navigation is supported via the parent Accordion

For items with complex content, ensure any interactive elements inside the panel are keyboard accessible.

```jsx live
<Accordion>
  <AccordionItem
    itemKey="1"
    title="Accessibility Settings"
    subtitle="Customize your experience"
    testID="accessibility-accordion"
  >
    <VStack gap={2}>
      <Text as="p" font="body" color="fgMuted">
        Adjust settings to make the app more accessible.
      </Text>
      <Button variant="secondary" compact accessibilityLabel="Open accessibility preferences">
        Open Preferences
      </Button>
    </VStack>
  </AccordionItem>
</Accordion>
```

### Refs

Use `headerRef` and `panelRef` to get references to the header button and panel elements for programmatic focus management or measurements.

```jsx
function RefExample() {
  const headerRef = useRef(null);
  const panelRef = useRef(null);

  const focusHeader = useCallback(() => {
    headerRef.current?.focus();
  }, []);

  return (
    <Accordion>
      <AccordionItem itemKey="1" title="Focusable Item" headerRef={headerRef} panelRef={panelRef}>
        <Text as="p" font="body" color="fgMuted">
          Panel content
        </Text>
      </AccordionItem>
    </Accordion>
  );
}
```

## Props

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `children` | `null \| string \| number \| bigint \| false \| true \| ReactElement<unknown, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal \| Promise<AwaitedReactNode>` | Yes | `-` | Collapsible content |
| `itemKey` | `string` | Yes | `-` | Key of the accordion item. This should be unique inside the same Accordion unless you want multiple items to be controlled at the same time. |
| `title` | `string` | Yes | `-` | Title of the accordion item |
| `headerRef` | `RefObject<HTMLButtonElement \| null>` | No | `-` | - |
| `maxHeight` | `ResponsiveProp<MaxHeight<string \| number>>` | No | `-` | - |
| `media` | `null \| string \| number \| bigint \| false \| true \| ReactElement<unknown, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal \| Promise<AwaitedReactNode>` | No | `-` | - |
| `onClick` | `((key: string) => void)` | No | `-` | Callback function fired when the accordion item is clicked |
| `panelRef` | `RefObject<HTMLDivElement \| null>` | No | `-` | - |
| `style` | `CSSProperties` | No | `-` | - |
| `subtitle` | `string` | No | `-` | Subtitle of the accordion item |
| `testID` | `string` | No | `-` | Used to locate this element in unit and end-to-end tests. Under the hood, testID translates to data-testid on Web. On Mobile, testID stays the same - testID |


