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
leadingIconProps['icon']Icon name rendered before the title
titleTranslationKeyHeader title
badgeReactElementBadge element rendered after the title, before the trailing icon
trailingIconProps['icon']dropdown chevronIcon name for the trailing indicator. Defaults to a chevron (ArrowUp2/ArrowDown2) reflecting active
onPress() => voidPress 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.