Lists are continuous, vertical indexes of text or images that are normally interactable.
A list can be created using the List and ListItem components.
import { List } from "@react-md/core/list/List";
import { ListItem } from "@react-md/core/list/ListItem";
import { type ReactElement } from "react";
export default function SimpleListExample(): ReactElement {
return (
<List>
<ListItem>Item 1</ListItem>
<ListItem>Item 2</ListItem>
<ListItem>Item 3</ListItem>
</List>
);
}
Each list item will normally have an onClick event handler to trigger an
action.
"use client";
import { List } from "@react-md/core/list/List";
import { ListItem } from "@react-md/core/list/ListItem";
import { type ReactElement } from "react";
export default function AddingClickHandlersExample(): ReactElement {
return (
<List>
<ListItem
onClick={() => {
// do something
}}
>
Item 1
</ListItem>
<ListItem
onClick={() => {
// do something
}}
>
Item 2
</ListItem>
</List>
);
}
The ListItem has different heights available and defaults to automatically
determining the height based on the provided props but can be manually set as
well. Check out the following examples to see the heights in action.
import { List } from "@react-md/core/list/List";
import { ListItem } from "@react-md/core/list/ListItem";
import { type ReactElement } from "react";
export default function DifferentSizesExample(): ReactElement {
return (
<List>
<ListItem height="auto">Auto (Default)</ListItem>
<ListItem height="normal">Normal</ListItem>
<ListItem height="medium">Medium</ListItem>
<ListItem height="large">Large</ListItem>
<ListItem height="extra-large">Extra Large</ListItem>
</List>
);
}
A ListItem normally contains primary content (children) and optionally
secondary content using the secondaryText prop. The secondary text will gain
the --rmd-text-secondary-color and use trailing ellipsis for long text. When
the secondaryText prop exists, the height will be automatically set to
"extra-large".
The secondaryText can also span multiple lines by enabling the multiline
prop. The default behavior will be to allow two lines of text and use
-webkit-line-clamp
to truncate the trailing text with ellipsis. The number of lines can be
configured by the core.$list-item-multiline-clamp Sass variable or
using the core.list-set-var(item-multiline-clamp, NEW_SIZE) mixin.
The ListItem also supports rendering addons before and after the children,
primaryText, and secondaryText using the leftAddon and rightAddon props.
The addon will default to being icon sized and positioned vertically centered
with the rest of the content.
import { List } from "@react-md/core/list/List";
import { ListItem } from "@react-md/core/list/ListItem";
import FavoriteIcon from "@react-md/material-icons/FavoriteIcon";
import VolumeOffOutlinedIcon from "@react-md/material-icons/VolumeOffOutlinedIcon";
import VolumeUpOutlinedIcon from "@react-md/material-icons/VolumeUpOutlinedIcon";
import { type ReactElement } from "react";
export default function ListItemAddonsExample(): ReactElement {
return (
<List>
<ListItem leftAddon={<FavoriteIcon />}>Left Addon</ListItem>
<ListItem rightAddon={<VolumeOffOutlinedIcon />}>Right Addon</ListItem>
<ListItem
leftAddon={<VolumeUpOutlinedIcon />}
rightAddon={<VolumeOffOutlinedIcon />}
secondaryText="But also secondary text"
>
Left and Right Addon
</ListItem>
</List>
);
}
When there is a leftAddon, there will normally be additional margin-right
applied so that the content align nicely with a hamburger menu and title in an
AppBar. This additional spacing can be removed by enabling
disableLeftAddonSpacing.
import { AppBar } from "@react-md/core/app-bar/AppBar";
import { AppBarTitle } from "@react-md/core/app-bar/AppBarTitle";
import { Button } from "@react-md/core/button/Button";
import { ICON_CONFIG } from "@react-md/core/icon/config";
import { List } from "@react-md/core/list/List";
import { ListItem } from "@react-md/core/list/ListItem";
import FavoriteIcon from "@react-md/material-icons/FavoriteIcon";
import VolumeUpOutlinedIcon from "@react-md/material-icons/VolumeUpOutlinedIcon";
import { type ReactElement } from "react";
export default function DisablingLeftAddonSpacingExample(): ReactElement {
return (
<div>
<AppBar>
<Button buttonType="icon" aria-label="Menu">
{ICON_CONFIG.menu}
</Button>
<AppBarTitle keyline="nav">Title</AppBarTitle>
</AppBar>
<List>
<ListItem leftAddon={<FavoriteIcon />}>Left Addon</ListItem>
<ListItem leftAddon={<VolumeUpOutlinedIcon />} disableLeftAddonSpacing>
Left Addon No Spacing
</ListItem>
</List>
</div>
);
}
When an addon should be an Avatar instead of an icon, set
the leftAddonType or rightAddonType to "avatar" to modify the positioning.
import { Avatar } from "@react-md/core/avatar/Avatar";
import { List } from "@react-md/core/list/List";
import { ListItem } from "@react-md/core/list/ListItem";
import FolderIcon from "@react-md/material-icons/FolderIcon";
import InfoOutlineIcon from "@react-md/material-icons/InfoOutlineIcon";
import { type ReactElement } from "react";
export default function AvatarAddonsExample(): ReactElement {
return (
<List>
<ListItem leftAddon={<Avatar>A</Avatar>} leftAddonType="avatar">
Left Avatar
</ListItem>
<ListItem rightAddon={<Avatar>A</Avatar>} rightAddonType="avatar">
Right Avatar with some additional text
</ListItem>
<ListItem
leftAddon={
<Avatar size="icon" color="purple">
I
</Avatar>
}
>
Icon-Sized Left Avatar
</ListItem>
<ListItem
leftAddon={
<Avatar color="blue">
<FolderIcon />
</Avatar>
}
leftAddonType="avatar"
secondaryText="Jan 04, 2019"
rightAddon={<InfoOutlineIcon />}
>
Photos
</ListItem>
</List>
);
}
When an addon should be an image or something around 3.5rem (56px), set the
leftAddonType or rightAddonType to "media". The media size and spacing can
be configured by the core.$list-item-media-size and
core.$list-item-media-spacing Sass variables.
import { List } from "@react-md/core/list/List";
import { ListItem } from "@react-md/core/list/ListItem";
import StarIcon from "@react-md/material-icons/StarIcon";
import { type ReactElement } from "react";
export default function MediaAddonsExample(): ReactElement {
return (
<List>
<ListItem
leftAddon={<img src="https://i.pravatar.cc/56?img=1" alt="" />}
leftAddonType="media"
>
Left Media
</ListItem>
<ListItem
rightAddon={<img src="https://i.pravatar.cc/56?img=4" alt="" />}
rightAddonType="media"
>
Right Media
</ListItem>
<ListItem
leftAddon={<img src="https://i.pravatar.cc/56?img=8" alt="" />}
leftAddonType="media"
rightAddon={<StarIcon />}
secondaryText="Scott Stirling. The Man. The Myth. The Legend."
multiline
>
{"See your video? You're a legend!"}
</ListItem>
</List>
);
}
The ListItem also supports large media that are 6.25rem (100px) wide by
setting the leftAddonType or rightAddonType to "large-media". The width
of the large media can be configured by the
core.$list-item-media-large-size Sass variable.
import { List } from "@react-md/core/list/List";
import { ListItem } from "@react-md/core/list/ListItem";
import { type ReactElement } from "react";
export default function LargeMediaAddonsExample(): ReactElement {
return (
<List>
<ListItem
leftAddon={<img src="https://picsum.photos/100/56?image=800" alt="" />}
leftAddonType="large-media"
>
Left Large Media
</ListItem>
<ListItem
leftAddon={<img src="https://picsum.photos/100/56?image=803" alt="" />}
leftAddonType="large-media"
secondaryText="With secondary text"
>
Left Large Media
</ListItem>
<ListItem
rightAddon={<img src="https://picsum.photos/100/56?image=700" alt="" />}
rightAddonType="large-media"
>
Right Large Media
</ListItem>
<ListItem
rightAddon={<img src="https://picsum.photos/100/56?image=738" alt="" />}
rightAddonType="large-media"
secondaryText="With secondary text"
>
Right Large Media
</ListItem>
</List>
);
}
The ListItem can be disabled by enabling the disabled prop which applies the
--rmd-text-disabled-color and prevents user interaction. Since the text color
doesn't apply to addons, the disabledOpacity prop can also be displayed to
apply opacity to the entire ListItem instead to show it has been disabled.
import { List } from "@react-md/core/list/List";
import { ListItem } from "@react-md/core/list/ListItem";
import FavoriteIcon from "@react-md/material-icons/FavoriteIcon";
import { type ReactElement } from "react";
export default function DisabledListItemExample(): ReactElement {
return (
<List>
<ListItem disabled>Disabled Item</ListItem>
<ListItem disabled disabledOpacity>
Disabled Item (Opacity)
</ListItem>
<ListItem disabled leftAddon={<FavoriteIcon />}>
Disabled Item
</ListItem>
<ListItem disabled disabledOpacity leftAddon={<FavoriteIcon />}>
Disabled Item (Opacity)
</ListItem>
</List>
);
}
If the ListItem should be used to only display data, set the role to
"presentation" removing all interaction behavior.
import { List } from "@react-md/core/list/List";
import { ListItem } from "@react-md/core/list/ListItem";
import { type ReactElement } from "react";
export default function NoninteractiveListExample(): ReactElement {
return (
<List>
<ListItem role="presentation">Item 1</ListItem>
<ListItem role="presentation">Item 2</ListItem>
<ListItem role="presentation">Item 3</ListItem>
</List>
);
}
The List and ListItem components remove the default <ol> and <ul>
styling, accessibility, and behavior to behave like a collection of actions
(buttons). If the default list styles should be used, replace the top-level
<ol> or <ul> with the Typography component instead so all items gain the
same typography.
import { Typography } from "@react-md/core/typography/Typography";
import { type ReactElement } from "react";
export default function DefaultListStylesExample(): ReactElement {
return (
<Typography as="ol">
<li>Read this list</li>
<li>Do something else</li>
<ul>
<li>Maybe read a book?</li>
<li>Or go on a hike?</li>
<li>Cook dinner?</li>
</ul>
<li>Celebrate</li>
</Typography>
);
}
Since the ListItem has some nice functionality of separating content with
addons, it might be tempting to use the ListItem for layouts instead of
interactions. This is not recommended for accessibility because most semantics
are removed from the List and ListItem components. Instead, use the Box
(or a flex container) and ListItemChildren components for presentational
data.
import { Box } from "@react-md/core/box/Box";
import { ListItemChildren } from "@react-md/core/list/ListItemChildren";
import FavoriteIcon from "@react-md/material-icons/FavoriteIcon";
import InfoOutlineIcon from "@react-md/material-icons/InfoOutlineIcon";
import { type ReactElement } from "react";
export default function ListItemChildrenExample(): ReactElement {
return (
<Box align="start" stacked disablePadding>
<Box style={{ "--rmd-box-gap": 0 }} disablePadding>
<ListItemChildren leftAddon={<FavoriteIcon />}>
This is my favorite!
</ListItemChildren>
</Box>
<div style={{ display: "flex" }}>
<ListItemChildren
leftAddon={<InfoOutlineIcon />}
rightAddon={<FavoriteIcon />}
secondaryText="Gotcha!"
>
Never mind, this might actually be my favorite!
</ListItemChildren>
</div>
</Box>
);
}