Skip to main content

Accordion

Collapsible panel with an animated header and content area. Expand/collapse state is controlled by the parent.

Basic Usage​

import { Accordion, Typography } from '@multinaire/expo-ui';
import { useState } from 'react';

function Example() {
const [active, setActive] = useState(false);

return (
<Accordion
active={active}
title="Section title"
onPress={() => setActive(!active)}
>
<Typography>Panel content</Typography>
</Accordion>
);
}

Props​

PropTypeDefaultDescription
activebooleanfalseWhen true, the panel content is expanded
leadingIconName—Icon name rendered before the title
titleTranslationKey—Header title
badgeReactElement—Badge element rendered after the title, before the trailing icon
trailingIconNamedropdown chevronIcon name for the trailing indicator. Defaults to a chevron (ArrowUp2/ArrowDown2) reflecting active
onPress() => void—Press handler for the header. When omitted, the header renders at reduced opacity to indicate it is non-interactive

Examples​

With Leading Icon​

<Accordion
active={active}
leading="InfoCircle"
title="More information"
onPress={() => setActive(!active)}
>
<Typography color="neutral">Additional details go here.</Typography>
</Accordion>

With Badge​

<Accordion
active={active}
title="Notifications"
badge={<Badge type="primary" title="3" />}
onPress={() => setActive(!active)}
>
<Typography>You have 3 unread notifications.</Typography>
</Accordion>

Custom Trailing Icon​

<Accordion
active={active}
title="Settings"
trailing="ArrowRight2"
onPress={() => setActive(!active)}
>
<Typography>Panel content</Typography>
</Accordion>

Non-Interactive Header​

<Accordion active title="Read-only section">
<Typography>This panel has no press handler, so the header renders dimmed.</Typography>
</Accordion>

Accordion Group​

function AccordionGroup() {
const [openIndex, setOpenIndex] = useState<number | null>(0);

return (
<Container gap={variables.gap.medium}>
{sections.map((section, index) => (
<Accordion
key={section.title}
active={openIndex === index}
title={section.title}
onPress={() =>
setOpenIndex(openIndex === index ? null : index)
}
>
<Typography>{section.content}</Typography>
</Accordion>
))}
</Container>
);
}

Notes​

  • Accordion is a controlled component — the parent owns active state and toggles it via onPress.
  • The panel is built on Card, so it inherits the theme's border, radius, and background.
  • Expand/collapse is animated with react-native-reanimated by measuring the content's height and animating to it, so surrounding layout doesn't jump.
  • The content is re-measured whenever it changes size, so children that load or grow after mount are handled automatically — an open panel animates to its new height, and a closed one opens to the correct height next time. A panel rendered with active already true starts open rather than animating open on mount.