Accordion is a container that can be expanded and collapsed to show content about a single subject. Its contents can be visible at all items, or expand and collapse as an individual item or a group of items.

also known as Section, Expandable Section, Module, Disclosure, Stack View, Expander

Figma:

Responsive:

Adaptive:

A11y:

Props

Component props
Name
Type
Default
id
Required
string
-

Unique id to identify this Accordion

badge
{
  text: string,
  type?:
    | "info"
    | "error"
    | "warning"
    | "success"
    | "neutral"
    | "recommendation"
    | "darkWash"
    | "lightWash",
}
-

Add a badge displayed after the title. Will not be displayed if title is not provided. Not to be used with icon or iconButton. Be sure to localize the text. See the badge variant for more details.

children
React.Node
-

Content to display underneath Accordion title.

icon
Icon[icon]
-

Name of icon to display in front of title. Will not be displayed if title is not provided. Not to be used with badge or iconButton. For a full list of icons, see Iconography and SVGs. See the icon variant for more details.

iconAccessibilityLabel
string
-

Label to provide information about the icon used for screen readers. Can be used in two scenarios: to describe the error icon that appears when type is error, and to describe the provided icon prop when type is info. Be sure to localize the label. See the icon variant for more details.

iconButton
React.Element<typeof IconButton | typeof IconButtonLink>
-

IconButton element to be placed after the title for a supplemental Call To Action (CTA). Will not be displayed if title is not provided. Not to be used with badge or icon. See the icon button variant for more details.

size
"sm" | "md" | "lg"
"lg"

Sets the size of the Accordion. See the size variant to learn more.

title
string
-

Title of this Accordion. Be sure to localize the text.

type
"error" | "info"
"info"

If set to error, displays error icon and changes title to red text. Be sure to provide an iconAccessibilityLabel when set to error. See the error variant for more details.

Usage guidelines

When to use
  • Grouping and organizing content to keep the page clean and digestible.
  • Displaying additional related content about a particular subject.
  • Enabling users to reveal or hide additional content as necessary (with Expandable variant).
When not to use
  • In a layout that conveys a clear sense of information hierarchy. Use SegmentedControl instead.
  • When long content can’t be displayed all at once, and scrolling is necessary.
  • When there is insufficient content to condense, as collapsing can increase cognitive load and interaction cost. Consider the static variant of Accordion.
  • When the content is crucial to read in full. Consider the static variant instead.

Accessibility

Localization

Be sure to localize all text strings. Note that localization can lengthen text by 20 to 30 percent.

Note that accessibilityCollapseLabel and accessibilityExpandLabel are optional as DefaultLabelProvider provides default strings. Use custom labels if they need to be more specific.

Accordion depends on DefaultLabelProvider for internal text strings. Localize the texts via DefaultLabelProvider. Learn more
import { Accordion, Box, DefaultLabelProvider, Text } from 'gestalt';

export default function Example() {
  return (
    <DefaultLabelProvider
      // $FlowExpectedError[incompatible-type] For demostration purposes
      labels={{
        Accordion: {
          accessibilityCollapseLabel: 'Abschnitt kollabieren.',
          accessibilityExpandLabel: 'Erweitern Sie den Abschnitt.',
        },
      }}
    >
      <Box
        alignItems="center"
        display="flex"
        height="100%"
        justifyContent="center"
        padding={1}
        width="100%"
      >
        <Box column={12} maxWidth={800} padding={2}>
          <Accordion.Expandable
            id="Accordion"
            items={[
              {
                children: <Text size="200">Inhalt</Text>,
                summary: [
                  'Zusammenfassung # 1',
                  'Zusammenfassung # 2',
                  'Zusammenfassung # 3',
                ],
                title: 'Titel',
              },
            ]}
          />
        </Box>
      </Box>
    </DefaultLabelProvider>
  );
}

Subcomponents

Accordion.Expandable

Use Accordion.Expandable if your accordion requires expanding and collapsing content.

Accordion.Expandable Props

Accordion.Expandable subcomponent props
Name
Type
Default
id
Required
string
-

Unique id to identify this Accordion. See Expandable variant to learn more.

items
Required
$ReadOnlyArray<{
  badge?: BadgeType,
  children?: React.Node,
  icon?: $Keys<typeof icons>,
  iconAccessibilityLabel?: string,
  iconButton?: React.Element<typeof IconButton>,
  summary?: $ReadOnlyArray<string>,
  title: string,
  type?: "error" | "info",
}>
-

Array of accordions displayed in a stack. Only one item can be expanded at a time. See Expandable variant to learn more.

accessibilityCollapseLabel
string
-

Label used to communicate to screen readers which accordion will be collapsed when interacting with the title button. Should be something clear, like "Collapse Security Policies Accordion". Be sure to localize the label. See Expandable variant to learn more.

accessibilityExpandLabel
string
-

Label used to communicate to screen readers which accordion will be expanded when interacting with the title button. Should be something clear, like "Expand Security Policies Accordion". Be sure to localize the label. See Expandable variant to learn more.

expandedIndex
?number
-

The 0-based index indicating the item that should currently be expanded. This must be updated via onExpandedChange to ensure the correct item is expanded. See Expandable variant to learn more.

onExpandedChange
(?number) => void
-

Callback executed whenever any accordion item is expanded or collapsed. It receives the index of the currently expanded accordion, or null if none are expanded. See Expandable variant to learn more.

size
"sm" | "md" | "lg"
"lg"

Size

Variants

Static

An Accordion is a container that can hold any content, and can optionally have a title that describes the content inside. The default, static Accordion is used to display information that should always be visible.

import { Accordion, Box, Flex, Text } from 'gestalt';

export default function Example() {
  return (
    <Box
      alignItems="center"
      display="flex"
      height="100%"
      justifyContent="center"
      padding={8}
    >
      <Flex
        direction="column"
        flex="grow"
        gap={{ column: 2, row: 0 }}
        maxWidth={800}
      >
        <Accordion id="accordionExample - default - 1">
          <Text size="200">This is example content.</Text>
        </Accordion>

        <Accordion id="accordionExample - default - 2" title="Title">
          <Text size="200">This is example content.</Text>
        </Accordion>
      </Flex>
    </Box>
  );
}

Static - Icon

An Icon can be provided to be placed before the title.

It is recommended that icons be used sparingly to convey additional information, and instead should simply reinforce information in the title. Be sure to provide an iconAccessibilityLabel.

import { Accordion, Box, Text } from 'gestalt';

export default function Example() {
  return (
    <Box
      alignItems="center"
      display="flex"
      height="100%"
      justifyContent="center"
      padding={8}
    >
      <Box column={12} maxWidth={800} padding={2}>
        <Accordion
          icon="lock"
          iconAccessibilityLabel="Accordion Locked - check permission settings"
          id="accordionExample - icon"
          title="Title"
        >
          <Text size="200">This is example content.</Text>
        </Accordion>
      </Box>
    </Box>
  );
}

Static - IconButton

An IconButton can be provided to be placed after the title for a supplemental Call To Action (CTA).

import { useRef, useState } from 'react';
import { Accordion, Box, IconButton, Popover, Text } from 'gestalt';

export default function Example() {
  const [showPopover, setShowPopover] = useState(false);
  const anchorRef = useRef(null);

  return (
    <Box
      alignItems="center"
      display="flex"
      height="100%"
      justifyContent="center"
      padding={8}
    >
      <Box column={12} maxWidth={800} padding={2}>
        <Accordion
          iconButton={
            <IconButton
              ref={anchorRef}
              accessibilityLabel="Get help"
              bgColor="lightGray"
              icon="question-mark"
              iconColor="darkGray"
              onClick={() => {
                setShowPopover((currVal) => !currVal);
              }}
              size="xs"
            />
          }
          id="accordionExample - iconButton"
          title="Title"
        >
          <Text size="200">This is example content.</Text>
        </Accordion>

        {showPopover && (
          <Popover
            anchor={anchorRef.current}
            idealDirection="right"
            onDismiss={() => setShowPopover(false)}
            shouldFocus={false}
          >
            <Box padding={3}>
              <Text weight="bold">Help content!</Text>
            </Box>
          </Popover>
        )}
      </Box>
    </Box>
  );
}

Static - Badge

Badge text can be provided, which will be displayed after the title. Note that if no title text is provided, the badge will not be displayed.

import { Accordion, Box, Text } from 'gestalt';

export default function Example() {
  return (
    <Box
      alignItems="center"
      display="flex"
      height="100%"
      justifyContent="center"
      padding={8}
    >
      <Box column={12} maxWidth={800} padding={2}>
        <Accordion
          badge={{ text: 'Beta' }}
          id="accordionExample - badge"
          title="Title"
        >
          <Text size="200">This is example content.</Text>
        </Accordion>
        <Accordion
          badge={{ text: 'Not started', type: 'neutral' }}
          id="accordionExample - badge neutral"
          title="Title"
        >
          <Text size="200">This is example content.</Text>
        </Accordion>
      </Box>
    </Box>
  );
}

Static - Error

When using type as "error", be sure to provide an iconAccessibilityLabel.

import { useState } from 'react';
import { Accordion, Box, Flex, Text, TextField } from 'gestalt';

export default function Example() {
  const [value, setValue] = useState('');

  return (
    <Box
      alignItems="center"
      display="flex"
      height="100%"
      justifyContent="center"
      padding={8}
    >
      <Box column={12} maxWidth={800} padding={2}>
        <Accordion
          iconAccessibilityLabel={
            !value ? 'This accordion contains an error' : undefined
          }
          id="accordionExample - error"
          title="Personal Info"
          type={!value ? 'error' : 'info'}
        >
          <Flex direction="column" gap={{ column: 4, row: 0 }}>
            <Text size="200">This is example content.</Text>

            <TextField
              errorMessage={!value ? "This field can't be blank!" : null}
              id="first-name"
              label="Enter Your Name"
              onChange={(e) => setValue(e.value)}
              value={value}
            />
          </Flex>
        </Accordion>
      </Box>
    </Box>
  );
}

Expandable

Accordions can also allow for expanding and collapsing content. The title is required and always present. The collapsed state shows optional summary content, while the expanded state shows any content desired.

import { Accordion, Box, Text } from 'gestalt';

export default function Example() {
  return (
    <Box
      alignItems="center"
      display="flex"
      height="100%"
      justifyContent="center"
      padding={8}
    >
      <Box column={12} maxWidth={800} padding={2}>
        <Accordion.Expandable
          id="ModuleExample - default"
          items={[
            {
              children: <Text size="200">Children1</Text>,
              summary: ['summary1', 'summary2', 'summary3'],
              title: 'Title',
            },
          ]}
        />
      </Box>
    </Box>
  );
}

Expandable - Group

Multiple expandable items can be stacked together into an Accordion group. However, only one Accordion will be expanded at any time.

import { Accordion, Box, Text } from 'gestalt';

export default function Example() {
  return (
    <Box
      alignItems="center"
      display="flex"
      height="100%"
      justifyContent="center"
      padding={8}
    >
      <Box column={12} maxWidth={800} padding={2}>
        <Accordion.Expandable
          id="AccordionExample "
          items={[
            {
              children: <Text size="200">Children1</Text>,
              summary: ['summary1'],
              title: 'Title1',
            },
            {
              children: <Text size="200">Children2</Text>,
              summary: ['summary2'],
              title: 'Title2',
            },
            {
              children: <Text size="200">Children3</Text>,
              summary: ['summary3'],
              title: 'Title3',
            },
          ]}
        />
      </Box>
    </Box>
  );
}

Expandable - Icon, Badge and IconButton

An Icon can be provided to be placed before the title.
It is recommended that icons be used sparingly to convey additional information, and instead should simply reinforce information in the title. Be sure to provide an iconAccessibilityLabel.

Badge text can also be provided, which will be displayed after the title.

An IconButton can be provided to be placed after the title for a supplemental Call To Action (CTA).

import { useRef, useState } from 'react';
import { Accordion, Box, IconButton, Popover, Text } from 'gestalt';

export default function Example() {
  const [showPopover, setShowPopover] = useState(false);
  const anchorRef = useRef(null);

  return (
    <Box
      alignItems="center"
      display="flex"
      height="100%"
      justifyContent="center"
      padding={8}
    >
      <Box column={12} maxWidth={800} padding={2}>
        <Accordion.Expandable
          id="accordionExample"
          items={[
            {
              children: <Text size="200">Children1</Text>,
              icon: 'lock',
              iconAccessibilityLabel: 'title icon',
              title: 'Example with icon',
            },
            {
              badge: { text: 'New' },
              children: <Text size="200">Children2</Text>,
              title: 'Example with badge',
            },
            {
              children: <Text size="200">Children3</Text>,
              iconButton: (
                <IconButton
                  ref={anchorRef}
                  accessibilityLabel="Get help"
                  bgColor="lightGray"
                  icon="question-mark"
                  iconColor="darkGray"
                  onClick={() => setShowPopover((currVal) => !currVal)}
                  size="xs"
                />
              ),
              title: 'Example with icon button',
            },
          ]}
        />

        {showPopover && (
          <Popover
            anchor={anchorRef.current}
            idealDirection="right"
            onDismiss={() => setShowPopover(false)}
            shouldFocus={false}
          >
            <Box padding={3}>
              <Text weight="bold">Help content!</Text>
            </Box>
          </Popover>
        )}
      </Box>
    </Box>
  );
}

Expandable - Error

When using type as "error", be sure to provide an iconAccessibilityLabel.

import { useState } from 'react';
import { Accordion, Box, Text, TextField } from 'gestalt';

export default function Example() {
  const [value, setValue] = useState('');
  const moduleType = !value ? 'error' : 'info';
  const summaryInfo = !value ? 'Name is missing' : `Name: ${value}`;
  const iconAccessibilityLabel = !value
    ? 'This accordion contains an error'
    : undefined;

  return (
    <Box
      alignItems="center"
      display="flex"
      height="100%"
      justifyContent="center"
      padding={8}
    >
      <Box column={12} maxWidth={800} padding={2}>
        <Accordion.Expandable
          id="accordionExample"
          items={[
            {
              children: (
                <Text size="200">
                  <TextField
                    errorMessage={!value ? "This field can't be blank!" : null}
                    id="aboutme"
                    label="Enter Your Name"
                    onChange={(e) => setValue(e.value)}
                    value={value}
                  />
                </Text>
              ),
              iconAccessibilityLabel,
              summary: [summaryInfo],
              title: 'Personal Info',
              type: moduleType,
            },
          ]}
        />
      </Box>
    </Box>
  );
}

Sizes

Module can have different sizes. The default size is large with a padding of 24px ($space-600). For a dense variant, use the sm size with a padding of 8px ($space-200).

import { useState } from 'react';
import {
  Accordion,
  Box,
  Flex,
  IconButton,
  SegmentedControl,
  Text,
} from 'gestalt';

export default function Example() {
  const sizes = ['sm', 'md', 'lg'];
  const [size, setSize] = useState('sm');

  return (
    <Box alignItems="center" display="flex" padding={8} width="100%">
      <Flex
        direction="column"
        gap={{ column: 2, row: 0 }}
        justifyContent="between"
        width="100%"
      >
        <SegmentedControl
          items={sizes}
          onChange={({ activeIndex }) => {
            setSize(sizes[activeIndex]);
          }}
          selectedItemIndex={sizes.indexOf(size)}
        />
        <Accordion
          badge={{ text: 'Beta' }}
          icon="lock"
          iconAccessibilityLabel="Accordion Locked - check permission settings"
          iconButton={
            <IconButton
              accessibilityLabel="Get help"
              bgColor="lightGray"
              icon="question-mark"
              iconColor="darkGray"
              onClick={() => {}}
              size="xs"
            />
          }
          id="AccordionExample - header"
          size={size}
          title="Title"
        >
          <Text size="200">This is example content.</Text>
        </Accordion>
        <Accordion.Expandable
          accessibilityCollapseLabel="Collapse section"
          accessibilityExpandLabel="Expand section"
          id="AccordionExample - header expandable"
          items={[
            {
              icon: 'lock',
              children: <Text size="200">Content here</Text>,
              summary: ['Summary 1', 'Summary 2'],
              title: 'Title',
            },
            {
              badge: { text: 'badge' },
              children: <Text size="200">Content here</Text>,
              summary: ['Summary 1', 'Summary 2', 'Summary 3'],
              title: 'Title',
            },
            {
              children: <Text size="200">More content here</Text>,
              summary: ['Summary 1', 'Summary 2'],
              title: 'Title',
              iconButton: (
                <IconButton
                  accessibilityLabel="Get help"
                  bgColor="lightGray"
                  icon="question-mark"
                  iconColor="darkGray"
                  onClick={() => {}}
                  size="xs"
                />
              ),
            },
          ]}
          size={size}
        />
      </Flex>
    </Box>
  );
}

Example with external control

import { useState } from 'react';
import { Accordion, Box, Flex, Text } from 'gestalt';

export default function Example() {
  const [extExpandedId, setExtExpandedId] = useState(null);
  const mapIds = {
    'first-0': 0,
    'first-1': 1,
    'second-0': 0,
    'second-1': 1,
  };

  return (
    <Box display="flex" height="100%" justifyContent="center" padding={8}>
      <Box column={12} maxWidth={800} padding={2}>
        <Flex direction="column" gap={{ column: 4, row: 0 }}>
          <Flex direction="column" gap={{ column: 2, row: 0 }}>
            <Box marginStart={2}>
              <Text>Step 1</Text>
            </Box>

            <Accordion.Expandable
              expandedIndex={
                typeof extExpandedId === 'string'
                  ? mapIds[extExpandedId]
                  : extExpandedId
              }
              id="ModuleExampleStep1"
              items={[
                {
                  title: 'Title1',
                  summary: ['summary1'],
                  children: <Text size="200">Children1</Text>,
                },
                {
                  title: 'Title2',
                  summary: ['summary2'],
                  children: <Text size="200">Children2</Text>,
                },
              ]}
              onExpandedChange={(index) => {
                if (index)
                  setExtExpandedId(
                    Number.isFinite(index) ? `first-${index}` : index
                  );
              }}
            />
          </Flex>

          <Flex direction="column" gap={{ column: 2, row: 0 }}>
            <Box marginStart={2}>
              <Text>Step 2</Text>
            </Box>

            <Accordion.Expandable
              expandedIndex={
                typeof extExpandedId === 'string'
                  ? mapIds[extExpandedId]
                  : extExpandedId
              }
              id="AccordionExampleStep2"
              items={[
                {
                  title: 'Title1',
                  summary: ['summary1'],
                  children: <Text size="200">Children1</Text>,
                },
                {
                  title: 'Title2',
                  summary: ['summary2'],
                  children: <Text size="200">Children2</Text>,
                },
              ]}
              onExpandedChange={(index) => {
                if (index)
                  setExtExpandedId(
                    Number.isFinite(index) ? `second-${index}` : index
                  );
              }}
            />
          </Flex>
        </Flex>
      </Box>
    </Box>
  );
}

Component quality checklist

Component quality checklist
Quality item
Status
Status description
Figma Library
Ready
Component is available in Figma for web and mobile web.
Responsive Web
Ready
Component responds to changing viewport sizes in web and mobile web.