Module is a container that holds content about one subject. Its contents can be visible at all times, or expand and collapse as individual modules or a group of modules.

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

Figma:

Responsive:

Adaptive:

A11y:

Props

Component props
Name
Type
Default
id
Required
string
-

Unique id to identify this Module

badge
{|
  text: string,
  type?: "info" | "error" | "warning" | "success" | "neutral" | "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 Module 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.

title
string
-

Title of this Module. 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 Module.
  • When the content is crucial to read in full. Consider the static variant instead.

Accessibility

Subcomponents

Module.Expandable

Use Module.Expandable if your module requires expanding and collapsing content.

Module.Expandable Props

Module.Expandable subcomponent props
Name
Type
Default
accessibilityCollapseLabel
Required
string
-

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

accessibilityExpandLabel
Required
string
-

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

id
Required
string
-

Unique id to identify this Module. 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 modules displayed in a stack. Only one item can be expanded at a time. 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 module item is expanded or collapsed. It receives the index of the currently expanded module, or null if none are expanded. See Expandable variant to learn more.

Variants

Static

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

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

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

        <Module id="ModuleExample - default - 2" title="Title">
          <Text size="200">This is example content.</Text>
        </Module>
      </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 { Box, Module, Text } from 'gestalt';

export default function Example() {
  return (
    <Box
      padding={8}
      height="100%"
      display="flex"
      alignItems="center"
      justifyContent="center"
    >
      <Box column={12} maxWidth={800} padding={2}>
        <Module
          icon="lock"
          iconAccessibilityLabel="Module Locked - check permission settings"
          id="ModuleExample - icon"
          title="Title"
        >
          <Text size="200">This is example content.</Text>
        </Module>
      </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 { Box, IconButton, Module, Popover, Text } from 'gestalt';

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

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

        {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 { Box, Module, Text } from 'gestalt';

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

Static - Error

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

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

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

  return (
    <Box
      padding={8}
      height="100%"
      display="flex"
      alignItems="center"
      justifyContent="center"
    >
      <Box column={12} maxWidth={800} padding={2}>
        <Module
          id="ModuleExample - error"
          title="Personal Info"
          iconAccessibilityLabel={
            !value ? 'This module contains an error' : undefined
          }
          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>
        </Module>
      </Box>
    </Box>
  );
}

Expandable

Modules 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 { Box, Module, Text } from 'gestalt';

export default function Example() {
  return (
    <Box
      padding={8}
      height="100%"
      display="flex"
      alignItems="center"
      justifyContent="center"
    >
      <Box column={12} maxWidth={800} padding={2}>
        <Module.Expandable
          accessibilityExpandLabel="Expand the module"
          accessibilityCollapseLabel="Collapse the module"
          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 a Module group. However, only one Module will be expanded at any time.

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

export default function Example() {
  return (
    <Box
      padding={8}
      height="100%"
      display="flex"
      alignItems="center"
      justifyContent="center"
    >
      <Box column={12} maxWidth={800} padding={2}>
        <Module.Expandable
          id="ModuleExample "
          accessibilityExpandLabel="Expand the module"
          accessibilityCollapseLabel="Collapse the module"
          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 { Box, IconButton, Module, Popover, Text } from 'gestalt';

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

  return (
    <Box
      padding={8}
      height="100%"
      display="flex"
      alignItems="center"
      justifyContent="center"
    >
      <Box column={12} maxWidth={800} padding={2}>
        <Module.Expandable
          accessibilityExpandLabel="Expand the module"
          accessibilityCollapseLabel="Collapse the module"
          id="ModuleExample "
          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
                  bgColor="lightGray"
                  icon="question-mark"
                  iconColor="darkGray"
                  accessibilityLabel="Get help"
                  size="xs"
                  onClick={() => setShowPopover((currVal) => !currVal)}
                  ref={anchorRef}
                />
              ),
              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 { Box, Module, 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 module contains an error'
    : undefined;

  return (
    <Box
      padding={8}
      height="100%"
      display="flex"
      alignItems="center"
      justifyContent="center"
    >
      <Box column={12} maxWidth={800} padding={2}>
        <Module.Expandable
          accessibilityExpandLabel="Expand the module"
          accessibilityCollapseLabel="Collapse the module"
          id="ModuleExample "
          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>
  );
}

Example with external control

import { useState } from 'react';
import { Box, Flex, Module, 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 padding={8} height="100%" display="flex" justifyContent="center">
      <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>

            <Module.Expandable
              accessibilityExpandLabel="Expand the module"
              accessibilityCollapseLabel="Collapse the module"
              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>

            <Module.Expandable
              id="ModuleExampleStep2"
              accessibilityExpandLabel="Expand the module"
              accessibilityCollapseLabel="Collapse the module"
              expandedIndex={
                typeof extExpandedId === 'string'
                  ? mapIds[extExpandedId]
                  : extExpandedId
              }
              onExpandedChange={(index) => {
                if (index)
                  setExtExpandedId(
                    Number.isFinite(index) ? `second-${index}` : index
                  );
              }}
              items={[
                {
                  title: 'Title1',
                  summary: ['summary1'],
                  children: <Text size="200">Children1</Text>,
                },
                {
                  title: 'Title2',
                  summary: ['summary2'],
                  children: <Text size="200">Children2</Text>,
                },
              ]}
            />
          </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.