Button Group
The ButtonGroup component can be used to group related buttons.
Basic button group
The buttons can be grouped by wrapping them with the
ButtonGroup component. They need to be immediate
children.
muiMaterialPage(
CssBaseline(),
ButtonGroup(variant = "contained", `aria-label` = "Basic button group", Button("One"), Button("Two"), Button("Three"))
)JS code
import Button from '@mui/material/Button';
import ButtonGroup from '@mui/material/ButtonGroup';
export default function BasicButtonGroup() {
return (
<ButtonGroup variant="contained" aria-label="Basic button group">
<Button>One</Button>
<Button>Two</Button>
<Button>Three</Button>
</ButtonGroup>
);
}Button variants
All the standard button variants are supported.
buttons <- tagList(Button("One"), Button("Two"), Button("Three"))
muiMaterialPage(
CssBaseline(),
Box(
sx = list(display = "flex", flexDirection = "column", alignItems = "center", "& > *" = list(m = 1)),
ButtonGroup(variant = "outlined", `aria-label` = "Basic button group", buttons),
ButtonGroup(variant = "text", `aria-label` = "Basic button group", buttons)
)
)JS code
import Button from '@mui/material/Button';
import ButtonGroup from '@mui/material/ButtonGroup';
import Box from '@mui/material/Box';
export default function VariantButtonGroup() {
return (
<Box
sx={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
'& > *': {
m: 1,
},
}}
>
<ButtonGroup variant="outlined" aria-label="Basic button group">
<Button>One</Button>
<Button>Two</Button>
<Button>Three</Button>
</ButtonGroup>
<ButtonGroup variant="text" aria-label="Basic button group">
<Button>One</Button>
<Button>Two</Button>
<Button>Three</Button>
</ButtonGroup>
</Box>
);
}Sizes and colors
The size and color props can be used to
control the appearance of the button group.
muiMaterialPage(
CssBaseline(),
Box(
sx = list(display = "flex", flexDirection = "column", alignItems = "center", "& > *" = list(m = 1)),
ButtonGroup(size = "small", `aria-label` = "Small button group", buttons),
ButtonGroup(color = "secondary", `aria-label` = "Medium-sized button group", buttons),
ButtonGroup(size = "large", `aria-label` = "Large button group", buttons)
)
)JS code
import Button from '@mui/material/Button';
import Box from '@mui/material/Box';
import ButtonGroup from '@mui/material/ButtonGroup';
const buttons = [
<Button key="one">One</Button>,
<Button key="two">Two</Button>,
<Button key="three">Three</Button>,
];
export default function GroupSizesColors() {
return (
<Box
sx={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
'& > *': {
m: 1,
},
}}
>
<ButtonGroup size="small" aria-label="Small button group">
{buttons}
</ButtonGroup>
<ButtonGroup color="secondary" aria-label="Medium-sized button group">
{buttons}
</ButtonGroup>
<ButtonGroup size="large" aria-label="Large button group">
{buttons}
</ButtonGroup>
</Box>
);
}Vertical group
The button group can be displayed vertically using the
orientation prop.
muiMaterialPage(
CssBaseline(),
Box(
sx = list(display = "flex", "& > *" = list(m = 1)),
ButtonGroup(orientation = "vertical", `aria-label` = "Vertical button group", buttons),
ButtonGroup(orientation = "vertical", `aria-label` = "Vertical button group", variant = "contained", buttons),
ButtonGroup(orientation = "vertical", `aria-label` = "Vertical button group", variant = "text", buttons)
)
)JS code
import Button from '@mui/material/Button';
import ButtonGroup from '@mui/material/ButtonGroup';
import Box from '@mui/material/Box';
const buttons = [
<Button key="one">One</Button>,
<Button key="two">Two</Button>,
<Button key="three">Three</Button>,
];
export default function GroupOrientation() {
return (
<Box
sx={{
display: 'flex',
'& > *': {
m: 1,
},
}}
>
<ButtonGroup orientation="vertical" aria-label="Vertical button group">
{buttons}
</ButtonGroup>
<ButtonGroup
orientation="vertical"
aria-label="Vertical button group"
variant="contained"
>
{buttons}
</ButtonGroup>
<ButtonGroup
orientation="vertical"
aria-label="Vertical button group"
variant="text"
>
{buttons}
</ButtonGroup>
</Box>
);
}Split button
ButtonGroup can also be used to create a split button.
The dropdown can change the button action (as in this example) or be
used to immediately trigger a related action.
The MUI demo positions a Popper() with a React ref. In
R, Menu.triggerId() opens the options next to the arrow
button (see Menu), and the selected option is
kept in the URL with reactRouter
(#/merge/<index>):
library(reactRouter)
options <- c("Create a merge commit", "Squash and merge", "Rebase and merge")
splitButton <- function(selectedIndex) {
tagList(
ButtonGroup(
variant = "contained",
`aria-label` = "Button group with a nested menu",
Button(onClick = JS(sprintf("() => { console.info('You clicked %s'); }", options[selectedIndex])), options[selectedIndex]),
Button(
id = "split-button-toggle",
size = "small",
`aria-label` = "select merge strategy",
`aria-haspopup` = "menu",
Icon("arrow_drop_down")
)
),
Menu.triggerId(
triggerId = "split-button-toggle",
id = "split-button-menu",
anchorOrigin = list(vertical = "bottom", horizontal = "right"),
transformOrigin = list(vertical = "top", horizontal = "right"),
lapply(seq_along(options), function(index) {
MenuItem(
component = "a",
href = paste0("#/merge/", index),
disabled = index == 3,
selected = index == selectedIndex,
options[index]
)
})
)
)
}
muiMaterialPage(
useMaterialIconsFilled = TRUE,
CssBaseline(),
RouterProvider(
router = createHashRouter(
lapply(1:2, function(index) Route(path = paste0("merge/", index), element = splitButton(index))),
Route(path = "*", element = splitButton(2))
)
)
)JS code
import * as React from 'react';
import Button from '@mui/material/Button';
import ButtonGroup from '@mui/material/ButtonGroup';
import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown';
import ClickAwayListener from '@mui/material/ClickAwayListener';
import Grow from '@mui/material/Grow';
import Paper from '@mui/material/Paper';
import Popper from '@mui/material/Popper';
import MenuItem from '@mui/material/MenuItem';
import MenuList from '@mui/material/MenuList';
const options = ['Create a merge commit', 'Squash and merge', 'Rebase and merge'];
export default function SplitButton() {
const [open, setOpen] = React.useState(false);
const anchorRef = React.useRef(null);
const [selectedIndex, setSelectedIndex] = React.useState(1);
const handleClick = () => {
console.info(`You clicked ${options[selectedIndex]}`);
};
const handleMenuItemClick = (event, index) => {
setSelectedIndex(index);
setOpen(false);
};
const handleToggle = () => {
setOpen((prevOpen) => !prevOpen);
};
const handleClose = (event) => {
if (anchorRef.current && anchorRef.current.contains(event.target)) {
return;
}
setOpen(false);
};
return (
<React.Fragment>
<ButtonGroup
variant="contained"
ref={anchorRef}
aria-label="Button group with a nested menu"
>
<Button onClick={handleClick}>{options[selectedIndex]}</Button>
<Button
size="small"
aria-controls={open ? 'split-button-menu' : undefined}
aria-expanded={open ? 'true' : undefined}
aria-label="select merge strategy"
aria-haspopup="menu"
onClick={handleToggle}
>
<ArrowDropDownIcon />
</Button>
</ButtonGroup>
<Popper
sx={{ zIndex: 1 }}
open={open}
anchorEl={anchorRef.current}
role={undefined}
transition
disablePortal
>
{({ TransitionProps, placement }) => (
<Grow
{...TransitionProps}
style={{
transformOrigin:
placement === 'bottom' ? 'center top' : 'center bottom',
}}
>
<Paper>
<ClickAwayListener onClickAway={handleClose}>
<MenuList id="split-button-menu" autoFocusItem>
{options.map((option, index) => (
<MenuItem
key={option}
disabled={index === 2}
selected={index === selectedIndex}
onClick={(event) => handleMenuItemClick(event, index)}
>
{option}
</MenuItem>
))}
</MenuList>
</ClickAwayListener>
</Paper>
</Grow>
)}
</Popper>
</React.Fragment>
);
}Disabled elevation
You can remove the elevation with the disableElevation
prop.
muiMaterialPage(
CssBaseline(),
ButtonGroup(disableElevation = TRUE, variant = "contained", `aria-label` = "Disabled button group", Button("One"), Button("Two"))
)JS code
Loading
Use the loading prop from Button to set
buttons in a loading state and disable interactions.
muiMaterialPage(
useMaterialIconsFilled = TRUE,
CssBaseline(),
ButtonGroup(
variant = "outlined",
`aria-label` = "Loading button group",
Button("Submit"),
Button("Fetch data"),
Button(loading = TRUE, loadingPosition = "start", startIcon = Icon("save"), "Save")
)
)JS code
import ButtonGroup from '@mui/material/ButtonGroup';
import Button from '@mui/material/Button';
import SaveIcon from '@mui/icons-material/Save';
export default function LoadingButtonGroup() {
return (
<ButtonGroup variant="outlined" aria-label="Loading button group">
<Button>Submit</Button>
<Button>Fetch data</Button>
<Button loading loadingPosition="start" startIcon={<SaveIcon />}>
Save
</Button>
</ButtonGroup>
);
}Button groups in Shiny apps
Each button of a group can be a Button.shinyInput(),
which reports its own click counter:
library(shiny)
library(muiMaterial)
ui <- muiMaterialPage(
CssBaseline(),
Box(
sx = list(p = 2),
ButtonGroup(
variant = "contained",
Button.shinyInput("one", "One"),
Button.shinyInput("two", "Two"),
Button.shinyInput("three", "Three")
),
verbatimTextOutput("clicked")
)
)
server <- function(input, output, session) {
last <- reactiveVal("none")
observeEvent(input$one, last("One"))
observeEvent(input$two, last("Two"))
observeEvent(input$three, last("Three"))
output$clicked <- renderText(paste("Last clicked:", last()))
}
shinyApp(ui, server)For a group of mutually exclusive options, use Toggle Button Group.
