Skip to main content
react-md

Expansion Panel

An expansion panel enables content to be either collapsed (hidden) or expanded (visible) following the disclosure pattern.

Simple Example

An ExpansionPanel is a required component requiring an expanded state and onExpandClick function to toggle the expanded state. The children will be dynamically shown whenever expanded is true. The toggle button's children can be defined using the headerChildren prop.

Contents
"use client";

import { ExpansionPanel } from "@react-md/core/expansion-panel/ExpansionPanel";
import { useToggle } from "@react-md/core/useToggle";
import { type ReactElement } from "react";

export default function SimpleExample(): ReactElement {
  const { toggled, toggle } = useToggle(true);

  return (
    <ExpansionPanel
      expanded={toggled}
      onExpandClick={toggle}
      headerChildren="Panel 1"
    >
      Contents
    </ExpansionPanel>
  );
}

Press Enter to start editing.

Expansion Panel Group

If multiple ExpansionPanels should be used together in a group, wrap them in the ExpansionList component to add additional keyboard accessibility. The useExpansionPanels hook can also be used to control the expanded state for each panel.

The useExpansionPanels hook will default to allowing only one panel to be visible at a time and all panels collapsed. Check out the following examples for additional useExpansionPanels options.

Panel 1 Contents
Panel 2 Contents
Panel 3 Contents
"use client";

import { ExpansionList } from "@react-md/core/expansion-panel/ExpansionList";
import { ExpansionPanel } from "@react-md/core/expansion-panel/ExpansionPanel";
import { useExpansionPanels } from "@react-md/core/expansion-panel/useExpansionPanels";
import { type ReactElement } from "react";

export default function ExpansionPanelGroupExample(): ReactElement {
  const { getPanelProps } = useExpansionPanels();

  return (
    <ExpansionList style={{ width: "100%" }}>
      <ExpansionPanel {...getPanelProps(0)} headerChildren="Panel 1">
        Panel 1 Contents
      </ExpansionPanel>
      <ExpansionPanel {...getPanelProps(1)} headerChildren="Panel 2">
        Panel 2 Contents
      </ExpansionPanel>
      <ExpansionPanel {...getPanelProps(2)} headerChildren="Panel 3">
        Panel 3 Contents
      </ExpansionPanel>
    </ExpansionList>
  );
}

Press Enter to start editing.

Multiple Expanded Panels

If multiple panels should be able to be expanded at the same time, enable the multiple option. This demo will also show how the defaultExpandedIndex can be used to expand a single panel by default.

Panel 1 Contents
Panel 2 Contents
Panel 3 Contents
"use client";

import { ExpansionList } from "@react-md/core/expansion-panel/ExpansionList";
import { ExpansionPanel } from "@react-md/core/expansion-panel/ExpansionPanel";
import { useExpansionPanels } from "@react-md/core/expansion-panel/useExpansionPanels";
import { type ReactElement } from "react";

export default function MultipleExpandedPanelsExample(): ReactElement {
  const { getPanelProps } = useExpansionPanels({
    multiple: true,
    defaultExpandedIndex: 1,
  });

  return (
    <ExpansionList style={{ width: "100%" }}>
      <ExpansionPanel {...getPanelProps(0)} headerChildren="Panel 1">
        Panel 1 Contents
      </ExpansionPanel>
      <ExpansionPanel {...getPanelProps(1)} headerChildren="Panel 2">
        Panel 2 Contents
      </ExpansionPanel>
      <ExpansionPanel {...getPanelProps(2)} headerChildren="Panel 3">
        Panel 3 Contents
      </ExpansionPanel>
    </ExpansionList>
  );
}

Press Enter to start editing.

Prevent All Panels Collapsed

The preventAllCollapsed option can be used to ensure at least one panel is always expanded and defaults to expanding the first panel.

Panel 1 Contents
Panel 2 Contents
Panel 3 Contents
"use client";

import { ExpansionList } from "@react-md/core/expansion-panel/ExpansionList";
import { ExpansionPanel } from "@react-md/core/expansion-panel/ExpansionPanel";
import { useExpansionPanels } from "@react-md/core/expansion-panel/useExpansionPanels";
import { type ReactElement } from "react";

export default function PreventAllPanelsCollapsedExample(): ReactElement {
  const { getPanelProps } = useExpansionPanels({
    preventAllCollapsed: true,

    // this can work with multiple expandable panels as well
    // multiple: true,

    // can set a custom default expanded index
    // defaultExpandedIndex: 1,
  });

  return (
    <ExpansionList style={{ width: "100%" }}>
      <ExpansionPanel {...getPanelProps(0)} headerChildren="Panel 1">
        Panel 1 Contents
      </ExpansionPanel>
      <ExpansionPanel {...getPanelProps(1)} headerChildren="Panel 2">
        Panel 2 Contents
      </ExpansionPanel>
      <ExpansionPanel {...getPanelProps(2)} headerChildren="Panel 3">
        Panel 3 Contents
      </ExpansionPanel>
    </ExpansionList>
  );
}

Press Enter to start editing.

String-based Panel Props

If the expansion panel should be identified by a unique id instead of an index, that unique id can be passed to getPanelProps instead. This also allows for specific panels to be expanded by default.

CaloriesFatCarbsProtein
1596244
CaloriesFatCarbsProtein
2379374.3
CaloriesFatCarbsProtein
26216376
CaloriesFatCarbsProtein
3053.7674.3
CaloriesFatCarbsProtein
35616493.9
CaloriesFatCarbsProtein
3750940
CaloriesFatCarbsProtein
3920.2980
CaloriesFatCarbsProtein
4083.2876.5
CaloriesFatCarbsProtein
5225514.9
CaloriesFatCarbsProtein
166657
"use client";

import { ExpansionList } from "@react-md/core/expansion-panel/ExpansionList";
import { ExpansionPanel } from "@react-md/core/expansion-panel/ExpansionPanel";
import { useExpansionPanels } from "@react-md/core/expansion-panel/useExpansionPanels";
import { Table } from "@react-md/core/table/Table";
import { TableBody } from "@react-md/core/table/TableBody";
import { TableCell } from "@react-md/core/table/TableCell";
import { TableContainer } from "@react-md/core/table/TableContainer";
import { TableHeader } from "@react-md/core/table/TableHeader";
import { TableRow } from "@react-md/core/table/TableRow";
import { type ReactElement } from "react";

export default function StringBasedPanelPropsExample(): ReactElement {
  const { getPanelProps } = useExpansionPanels({
    multiple: true,
    defaultExpandedIds: () => desserts.map(({ name }) => name),
  });
  return (
    <ExpansionList style={{ width: "100%" }}>
      {desserts.map((dessert) => {
        const { name } = dessert;
        return (
          <ExpansionPanel
            {...getPanelProps(name)}
            key={name}
            headerChildren={`${name} nutrition`}
            disableContentPadding
          >
            <NutritionFacts {...dessert} />
          </ExpansionPanel>
        );
      })}
    </ExpansionList>
  );
}

function NutritionFacts(props: Dessert): ReactElement {
  const { calories, fat, carbs, protein } = props;

  return (
    <TableContainer>
      <Table>
        <TableHeader>
          <TableRow>
            <TableCell>Calories</TableCell>
            <TableCell>Fat</TableCell>
            <TableCell>Carbs</TableCell>
            <TableCell>Protein</TableCell>
          </TableRow>
        </TableHeader>
        <TableBody>
          <TableRow>
            <TableCell>{calories}</TableCell>
            <TableCell>{fat}</TableCell>
            <TableCell>{carbs}</TableCell>
            <TableCell>{protein}</TableCell>
          </TableRow>
        </TableBody>
      </Table>
    </TableContainer>
  );
}

export interface Dessert {
  name: string;
  calories: number;
  fat: number;
  carbs: number;
  protein: number;
  sodium: number;
  calcium: number;
  iron: number;
  type: "Ice cream" | "Pastry" | "Other";
}

export type DessertKey = keyof Dessert;

export const desserts: readonly Dessert[] = [
  {
    name: "Frozen yogurt",
    type: "Ice cream",
    calories: 159,
    fat: 6.0,
    carbs: 24,
    protein: 4.0,
    sodium: 87,
    calcium: 14,
    iron: 1,
  },
  {
    name: "Ice cream sandwich",
    type: "Ice cream",
    calories: 237,
    fat: 9.0,
    carbs: 37,
    protein: 4.3,
    sodium: 129,
    calcium: 8,
    iron: 1,
  },
  {
    name: "Eclair",
    type: "Pastry",
    calories: 262,
    fat: 16.0,
    carbs: 37,
    protein: 6.0,
    sodium: 337,
    calcium: 6,
    iron: 7,
  },
  {
    name: "Cupcake",
    type: "Pastry",
    calories: 305,
    fat: 3.7,
    carbs: 67,
    protein: 4.3,
    sodium: 413,
    calcium: 3,
    iron: 8,
  },
  {
    name: "Gingerbread",
    type: "Pastry",
    calories: 356,
    fat: 16.0,
    carbs: 49,
    protein: 3.9,
    sodium: 327,
    calcium: 7,
    iron: 16,
  },
  {
    name: "Jelly bean",
    type: "Other",
    calories: 375,
    fat: 0.0,
    carbs: 94,
    protein: 0.0,
    sodium: 50,
    calcium: 0,
    iron: 0,
  },
  {
    name: "Lollipop",
    type: "Other",
    calories: 392,
    fat: 0.2,
    carbs: 98,
    protein: 0.0,
    sodium: 38,
    calcium: 0,
    iron: 2,
  },
  {
    name: "Honeycomb",
    type: "Other",
    calories: 408,
    fat: 3.2,
    carbs: 87,
    protein: 6.5,
    sodium: 562,
    calcium: 0,
    iron: 45,
  },
  {
    name: "Donut",
    type: "Pastry",
    calories: 52,
    fat: 25.0,
    carbs: 51,
    protein: 4.9,
    sodium: 326,
    calcium: 2,
    iron: 22,
  },
  {
    name: "KitKat",
    type: "Other",
    calories: 16,
    fat: 6.0,
    carbs: 65,
    protein: 7.0,
    sodium: 54,
    calcium: 12,
    iron: 6,
  },
];

export const dessertColumns = Object.keys(desserts[0]) as readonly DessertKey[];

Press Enter to start editing.

Disable Transition

The collapse transition can be disabled by enabling the disableTransition prop.

Contents
"use client";

import { ExpansionPanel } from "@react-md/core/expansion-panel/ExpansionPanel";
import { useToggle } from "@react-md/core/useToggle";
import { type ReactElement } from "react";

export default function DisableTransitionExample(): ReactElement {
  const { toggled, toggle } = useToggle(true);

  return (
    <ExpansionPanel
      expanded={toggled}
      onExpandClick={toggle}
      headerChildren="Panel 1"
      disableTransition
    >
      Contents
    </ExpansionPanel>
  );
}

Press Enter to start editing.

Temporary Contents

The ExpansionPanel will hide collapsed content by using display: none but can dynamically render it instead by enabling the temporary prop. Dynamically rendering can be helpful if the children's state should reset each time it becomes visible.

"use client";

import { ExpansionPanel } from "@react-md/core/expansion-panel/ExpansionPanel";
import { TextField } from "@react-md/core/form/TextField";
import { useToggle } from "@react-md/core/useToggle";
import { type ReactElement } from "react";

export default function TemporaryContentsExample(): ReactElement {
  const { toggled, toggle } = useToggle(true);

  return (
    <ExpansionPanel
      expanded={toggled}
      onExpandClick={toggle}
      headerChildren="Panel"
      temporary
    >
      <PanelContents />
    </ExpansionPanel>
  );
}

function PanelContents(): ReactElement {
  return <TextField label="Label" />;
}

Press Enter to start editing.