Skip to main content
react-md

Button

Buttons communicate actions that users can take. They are typically placed throughout your UI, in places like:

Simple Button

The Button comes in multiple theme types: flat (default), outlined, contained

import { Button } from "@react-md/core/button/Button";
import { type ReactElement } from "react";

export default function SimpleButton(): ReactElement {
  return (
    <>
      <Button themeType="flat">Button</Button>
      <Button themeType="outline">Button</Button>
      <Button themeType="contained">Button</Button>
    </>
  );
}

Press Enter to start editing.

Text Button

Text buttons are typically used for less-pronounced actions, including those located:

In cards, text buttons help maintain an emphasis on card content.

import { Button } from "@react-md/core/button/Button";
import { type ReactElement } from "react";

export default function TextButton(): ReactElement {
  return (
    <>
      <Button>Clear</Button>
      <Button theme="primary">Primary</Button>
      <Button theme="secondary">Secondary</Button>
    </>
  );
}

Press Enter to start editing.

Outlined Button

Outlined buttons are medium-emphasis buttons. They contain actions that are important, but aren’t the primary action in an app.

import { Button } from "@react-md/core/button/Button";
import { type ReactElement } from "react";

export default function OutlinedButton(): ReactElement {
  return (
    <>
      <Button themeType="outline">Clear</Button>
      <Button themeType="outline" theme="primary">
        Primary
      </Button>
      <Button themeType="outline" theme="secondary">
        Secondary
      </Button>
    </>
  );
}

Press Enter to start editing.

Contained Button

Contained buttons Contained buttons are high-emphasis, distinguished by their use of elevation and fill. They contain actions that are primary to your app.

import { Button } from "@react-md/core/button/Button";
import { type ReactElement } from "react";

export default function ContainedButton(): ReactElement {
  return (
    <>
      <Button themeType="contained">Clear</Button>
      <Button themeType="contained" theme="primary">
        Primary
      </Button>
      <Button themeType="contained" theme="secondary">
        Secondary
      </Button>
    </>
  );
}

Press Enter to start editing.

Themed Button

The Button can also be themed with different theme colors:

The theme will be applied as the text color for flat buttons, as the text color and border color for outlined buttons, and the background color for contained buttons.

import { Button } from "@react-md/core/button/Button";
import FavoriteIcon from "@react-md/material-icons/FavoriteIcon";
import { type ReactElement } from "react";

export default function ThemedButton(): ReactElement {
  return (
    <>
      <Button>Clear</Button>
      <Button theme="primary">Primary</Button>
      <Button theme="secondary">Secondary</Button>
      <Button theme="warning" themeType="outline">
        Warning
      </Button>
      <Button theme="success" themeType="contained">
        Success
        <FavoriteIcon />
      </Button>
      <Button theme="error" themeType="contained">
        <FavoriteIcon />
        Error
      </Button>
      <Button disabled>Disabled</Button>
    </>
  );
}

Press Enter to start editing.

Icon Button

An icon button can be used when space is limited like on mobile devices or when there are a lot of actions. Icon buttons support all the same theme behavior as text buttons and can be rendered as a circle (default) or as a square.

To create an icon button, set the buttonType prop to "icon" or "icon-square".

Icon buttons must provide accessible text for screen readers through aria-label, aria-labelledby, or SrOnly text.

import { Button } from "@react-md/core/button/Button";
import { SrOnly } from "@react-md/core/typography/SrOnly";
import FavoriteIcon from "@react-md/material-icons/FavoriteIcon";
import { type ReactElement } from "react";

export default function IconButton(): ReactElement {
  return (
    <>
      <Button aria-label="Favorite" buttonType="icon">
        <FavoriteIcon />
      </Button>
      <Button theme="secondary" themeType="outline" buttonType="icon">
        <SrOnly>Favorite</SrOnly>
        <FavoriteIcon />
      </Button>
      <Button
        aria-label="Favorite"
        theme="primary"
        themeType="contained"
        buttonType="icon"
      >
        <FavoriteIcon />
      </Button>
      <Button aria-label="Favorite" buttonType="icon-square">
        <FavoriteIcon />
      </Button>
      <Button
        aria-label="Favorite"
        theme="secondary"
        themeType="outline"
        buttonType="icon-square"
      >
        <FavoriteIcon />
      </Button>
      <Button
        aria-label="Favorite"
        theme="primary"
        themeType="contained"
        buttonType="icon-square"
      >
        <FavoriteIcon />
      </Button>
    </>
  );
}

Press Enter to start editing.

Icon Sizes

The icon button's size can be easily changed by updating the font-size to a new value which will automatically scale the padding with it. There are a few sizes by default: small, normal (default), and large.

Using small icon buttons will be less accessible since there is a smaller touch target.

import { Button } from "@react-md/core/button/Button";
import FavoriteIcon from "@react-md/material-icons/FavoriteIcon";
import { type ReactElement } from "react";

export default function IconButtonSizes(): ReactElement {
  return (
    <>
      <Button aria-label="Favorite" iconSize="small" buttonType="icon">
        <FavoriteIcon />
      </Button>
      <Button aria-label="Favorite" iconSize="normal" buttonType="icon">
        <FavoriteIcon />
      </Button>
      <Button aria-label="Favorite" iconSize="large" buttonType="icon">
        <FavoriteIcon />
      </Button>
      <Button
        aria-label="Favorite"
        buttonType="icon"
        style={{ fontSize: "2rem" }}
      >
        <FavoriteIcon />
      </Button>
    </>
  );
}

Press Enter to start editing.

Button with Text and Icon

Text and icons can both exist within a Button by just including them in the children. Each element rendered in a button will automatically be separated using gap set to the core.$icon-spacing amount.

An aria-label, aria-labelledby, or SrOnly text is not needed for each icon in this sort of button since it is only presentational.

import { Button } from "@react-md/core/button/Button";
import FavoriteIcon from "@react-md/material-icons/FavoriteIcon";
import { type ReactElement } from "react";

export default function ButtonWithTextAndIcon(): ReactElement {
  return (
    <>
      <Button>
        <FavoriteIcon />
        Text
      </Button>
      <Button theme="primary" themeType="outline">
        Text
        <FavoriteIcon />
      </Button>
      <Button theme="secondary" themeType="contained">
        <FavoriteIcon />
        Text
        <FavoriteIcon />
      </Button>
    </>
  );
}

Press Enter to start editing.

Responsive Button

A common pattern is to have a button be icon only on phones due to the limited screen size and then include a label as well once there is enough space. This can be done by enabling the responsive prop on the button and wrapping the label in the SrOnly component with the phoneOnly prop enabled:

import { Button } from "@react-md/core/button/Button";
import { SrOnly } from "@react-md/core/typography/SrOnly";
import FavoriteIcon from "@react-md/material-icons/FavoriteIcon";
import { type ReactElement } from "react";

export default function ResponsiveButton(): ReactElement {
  return (
    <>
      <Button responsive>
        <FavoriteIcon />
        <SrOnly phoneOnly>Favorite</SrOnly>
      </Button>
      <Button responsive buttonType="icon">
        <FavoriteIcon />
        <SrOnly phoneOnly>Favorite</SrOnly>
      </Button>
      <Button responsive buttonType="icon-square">
        <FavoriteIcon />
        <SrOnly phoneOnly>Favorite</SrOnly>
      </Button>
      <Button responsive buttonType="icon-square">
        <SrOnly phoneOnly>Favorite</SrOnly>
        <FavoriteIcon />
      </Button>
    </>
  );
}

Press Enter to start editing.

Floating Action Button

Floating action buttons performs the primary, or most common, action on a screen. It appears in front of all screen content, typically as a circular shape with an icon in its center.

To create a floating action button, set the floating prop to one of: top-left, top-right, bottom-left, or bottom-right while will position the button in that location within the screen by using position: fixed. When the floating prop is defined, the button default props will be updated with theme="secondary", themeType="contained", and buttonType="icon".

import { Button } from "@react-md/core/button/Button";
import FavoriteIcon from "@react-md/material-icons/FavoriteIcon";
import { type ReactElement } from "react";

export default function FloatingActionButton(): ReactElement {
  return (
    <>
      <Button aria-label="Favorite" floating="top-left">
        <FavoriteIcon />
      </Button>
      <Button aria-label="Favorite" floating="top-right" theme="primary">
        <FavoriteIcon />
      </Button>
      <Button floating="bottom-left" themeType="outline" buttonType="text">
        Action
      </Button>
      <Button floating="bottom-right" themeType="flat" buttonType="text">
        Action
      </Button>
    </>
  );
}

Press Enter to start editing.

Async Button

The AsyncButton can be used to display a loading indicator inside a button while an async task completes. If the onClick handler is an async function, the loading indicator will display until the promise resolves.

"use client";

import { AsyncButton } from "@react-md/core/button/AsyncButton";
import { wait } from "@react-md/core/utils/wait";
import { type ReactElement } from "react";

export default function AsyncButtonPromise(): ReactElement {
  return (
    <>
      <AsyncButton onClick={() => wait(4000)}>Click Me</AsyncButton>
      <AsyncButton
        onClick={() => wait(4000)}
        theme="primary"
        themeType="outline"
      >
        Click Me
      </AsyncButton>
      <AsyncButton
        onClick={() => wait(4000)}
        theme="secondary"
        themeType="contained"
      >
        Click Me
      </AsyncButton>
    </>
  );
}

Press Enter to start editing.

Manual Pending State

When the pending state is derived by a separate action, the loading indicator can be shown by setting the loading prop to true:

"use client";

import { Box } from "@react-md/core/box/Box";
import { AsyncButton } from "@react-md/core/button/AsyncButton";
import { Switch } from "@react-md/core/form/Switch";
import { type ReactElement, useState } from "react";

export default function ManualLoadingState(): ReactElement {
  const [loading, setLoading] = useState(false);

  return (
    <Box stacked align="stretch">
      <Switch
        label="Loading?"
        checked={loading}
        onChange={(event) => {
          setLoading(event.currentTarget.checked);
        }}
      />
      <AsyncButton loading={loading} theme="clear" themeType="flat">
        Button
      </AsyncButton>
      <AsyncButton loading={loading} theme="primary" themeType="outline">
        Button
      </AsyncButton>
      <AsyncButton loading={loading} theme="secondary" themeType="contained">
        Button
      </AsyncButton>
    </Box>
  );
}

Press Enter to start editing.

Loading Indicator Types

The AsyncButton supports the following loading indicator behaviors out of the box:

"use client";

import { Box } from "@react-md/core/box/Box";
import { AsyncButton } from "@react-md/core/button/AsyncButton";
import { Switch } from "@react-md/core/form/Switch";
import FavoriteIcon from "@react-md/material-icons/FavoriteIcon";
import { type ReactElement, useState } from "react";

export default function LoadingIndicatorTypes(): ReactElement {
  const [loading, setLoading] = useState(false);

  return (
    <Box stacked align="stretch">
      <Switch
        label="Loading?"
        checked={loading}
        onChange={(event) => {
          setLoading(event.currentTarget.checked);
        }}
      />
      <AsyncButton
        loading={loading}
        theme="secondary"
        themeType="flat"
        loadingType="circular-overlay"
      >
        Circular Overlay
      </AsyncButton>
      <AsyncButton
        loading={loading}
        theme="clear"
        themeType="outline"
        loadingType="linear-above"
      >
        Linear Above
      </AsyncButton>
      <AsyncButton
        loading={loading}
        theme="secondary"
        themeType="outline"
        loadingType="linear-below"
      >
        Linear Below
      </AsyncButton>
      <AsyncButton
        loading={loading}
        theme="primary"
        themeType="contained"
        loadingType="circular-before"
        beforeAddon={<FavoriteIcon />}
      >
        Circular Before
      </AsyncButton>
      <AsyncButton
        loading={loading}
        theme="warning"
        themeType="contained"
        loadingType="circular-after"
        afterAddon={<FavoriteIcon />}
      >
        Circular After
      </AsyncButton>
    </Box>
  );
}

Press Enter to start editing.

Custom Loading Children

If the children should be swapped out along with the loading indicator, provide that content through the loadingChildren prop.

"use client";

import { AsyncButton } from "@react-md/core/button/AsyncButton";
import { wait } from "@react-md/core/utils/wait";
import FavoriteIcon from "@react-md/material-icons/FavoriteIcon";
import { type ReactElement } from "react";

export default function CustomLoadingChildren(): ReactElement {
  return (
    <>
      <AsyncButton
        onClick={() => wait(4000)}
        loadingType="linear-below"
        loadingChildren="Loading..."
        loadingDisabledTheme
      >
        Click Me
      </AsyncButton>
      <AsyncButton
        onClick={() => wait(4000)}
        theme="primary"
        themeType="outline"
        loadingType="circular-before"
        loadingChildren="Loading..."
        loadingDisabledTheme
        beforeAddon={<FavoriteIcon />}
      >
        Click Me
      </AsyncButton>
    </>
  );
}

Press Enter to start editing.