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.
"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>
);
}
Expansion Panel Group
If multiple ExpansionPanel
s 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.
"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>
);
}
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.
"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>
);
}
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.
"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>
);
}
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.
"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[];
Disable Transition
The collapse transition can be disabled by enabling the disableTransition
prop.
"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>
);
}
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" />;
}