Button

Import
import { Button } from "reshaped";
import type { ButtonProps } from "reshaped";
Related components

Button accepts the children property to render its content. This can include any content, from text to a group of elements stacked together inside the Button.

<Button onClick={() => {}}>About us</Button>

Button works with both href and onClick properties. Using either will render the Button as an <a> or <button> tag, respectively.

<View gap={3} direction="row">
  <Button href="/" attributes={{ target: "_blank" }}>
    Back to main page
  </Button>

  <Button onClick={() => {}}>Sign in</Button>
</View>

You can use Button with all popular router solutions by wrapping it in the router's Link component.

<RouterLink href="/">
  <Button>Proceed to checkout</Button>
</RouterLink>

Button supports multiple variants that change its visual style. Neutral buttons support three variants, while colored buttons also support the faded variant.

<View gap={3} direction="row" align="center">
  <Button onClick={() => {}}>Solid button</Button>
  <Button variant="outline" onClick={() => {}}>
    Outline button
  </Button>
  <Button variant="ghost" onClick={() => {}}>
    Ghost button
  </Button>
</View>

Button uses a neutral color by default but supports other colors through the color property. This allows you to emphasize your main call-to-action with a primary color, draw attention to a critical action, or highlight a positive action.

<View gap={3}>
  <View gap={3} direction="row" align="center">
    <Button color="primary" onClick={() => {}}>
      Solid button
    </Button>
    <Button color="primary" variant="faded" onClick={() => {}}>
      Faded button
    </Button>
    <Button color="primary" variant="outline" onClick={() => {}}>
      Outline button
    </Button>
    <Button color="primary" variant="ghost" onClick={() => {}}>
      Ghost button
    </Button>
  </View>

  <View gap={3} direction="row" align="center">
    <Button color="critical" onClick={() => {}}>
      Solid button
    </Button>
    <Button color="critical" variant="faded" onClick={() => {}}>
      Faded button
    </Button>
    <Button color="critical" variant="outline" onClick={() => {}}>
      Outline button
    </Button>
    <Button color="critical" variant="ghost" onClick={() => {}}>
      Ghost button
    </Button>
  </View>

  <View gap={3} direction="row" align="center">
    <Button color="positive" onClick={() => {}}>
      Solid button
    </Button>
    <Button color="positive" variant="faded" onClick={() => {}}>
      Faded button
    </Button>
    <Button color="positive" variant="outline" onClick={() => {}}>
      Outline button
    </Button>
    <Button color="positive" variant="ghost" onClick={() => {}}>
      Ghost button
    </Button>
  </View>
</View>

Use the media button color when displaying a button over media content. It uses static color tokens, meaning it looks the same in light and dark mode. This is important for media content since images and videos preserve their colors in both modes.

<View gap={3} direction="row">
  <View.Item columns={6}>
    <View borderRadius="medium" overflow="hidden" aspectRatio={16 / 9}>
      <Image src="/img/examples/image-retina.webp" />
      <div style={{ position: "absolute", top: 8, left: 8 }}>
        <Button
          color="media"
          variant="faded"
          icon={IconHeart}
          onClick={() => {}}
        />
      </div>
    </View>
  </View.Item>
  <View.Item columns={6}>
    <Scrim
      position="top"
      borderRadius="medium"
      backgroundSlot={
        <View aspectRatio={16 / 9}>
          <Image src="/img/examples/image-retina.webp" />
        </View>
      }
    >
      <Button color="media" icon={IconZap} onClick={() => {}} />
    </Scrim>
  </View.Item>
</View>

When rendering a Button on top of a dynamic color, you can use the inherit color to automatically adopt the current text color for the Button styles. This could be helpful when you're rendering it on a primary background that changes based on the theme or when your component supports multiple background colors but you want to use the same Button component. In the following example, we're using the inherit color on top of primary and neutral colors, so both Buttons stay white in dark mode, but one of them turns black in light mode.

<View gap={3} direction="column">
  <View
    padding={4}
    backgroundColor="primary"
    borderRadius="small"
    direction="row"
    gap={2}
  >
    <Button color="inherit" variant="ghost">
      Inherit ghost
    </Button>
    <Button color="inherit" variant="outline">
      Inherit outline
    </Button>
    <Button color="inherit" variant="faded">
      Inherit faded
    </Button>
  </View>
  <View
    padding={4}
    backgroundColor="neutral"
    borderRadius="small"
    direction="row"
    gap={2}
  >
    <Button color="inherit" variant="ghost">
      Inherit ghost
    </Button>
    <Button color="inherit" variant="outline">
      Inherit outline
    </Button>
    <Button color="inherit" variant="faded">
      Inherit faded
    </Button>
  </View>
</View>

Button comes in four sizes, with the medium size used by default. small size can be used when there is limited space available for rendering, such as in complex tools or dashboards. large and xlarge sizes are usually used in mobile layouts and on marketing pages.

<View gap={3} direction="row">
  <Button size="small" onClick={() => {}}>
    Save progress
  </Button>
  <Button size="medium" onClick={() => {}}>
    Sign in
  </Button>
  <Button size="large" onClick={() => {}}>
    Get started
  </Button>
  <Button size="xlarge" onClick={() => {}}>
    Subscribe
  </Button>
</View>

Button width is defined by its content. To stretch the Button to the entire width of its parent element, you can use the fullWidth property. For example, you can use this property on mobile viewports where most buttons usually take the entire width of the page.

<Button fullWidth color="primary" onClick={() => {}}>
  Download application
</Button>

If Button is used for asynchronous actions, use the loading property to provide feedback to the user. Turning the loading state on will prevent Button click events without disabling it visually.

<View gap={3} direction="row">
  <Button loading onClick={() => {}}>
    Sign in
  </Button>
  <Button color="critical" loading onClick={() => {}}>
    Delete message
  </Button>
  <Button variant="ghost" loading onClick={() => {}}>
    Load more
  </Button>
</View>

Buttons used for actions can also be disabled from user input with a disabled flag. This will completely prevent the user from interacting with the Button.

<Button disabled onClick={() => {}}>
  Sign in
</Button>

An icon can be rendered on either side of the Button text content with the icon and endIcon properties. These properties automatically wrap the SVG you pass to them with an Icon utility.

<View gap={3} direction="row">
  <Button icon={IconZap} onClick={() => {}}>
    1-click order
  </Button>
  <Button endIcon={IconZap} onClick={() => {}}>
    1-click order
  </Button>
</View>

If there is not enough space to display a text label, the Button can be rendered with just an icon. In this case, make sure to also pass the aria-label attribute to the Button or wrap it with a Tooltip component to keep it accessible for screen readers.

<View gap={3} direction="row">
  <Button
    icon={IconZap}
    attributes={{ "aria-label": "1-click order" }}
    onClick={() => {}}
  />
  <Button
    icon={IconZap}
    variant="ghost"
    attributes={{ "aria-label": "1-click order" }}
    onClick={() => {}}
  />
</View>

Change the Button radius to a circular value by using the rounded flag for both buttons with text and icon-only buttons.

<View gap={3} direction="row">
  <Button rounded onClick={() => {}}>
    Edit profile
  </Button>
  <Button
    rounded
    icon={IconZap}
    variant="faded"
    color="primary"
    attributes={{ "aria-label": "1-click order" }}
    onClick={() => {}}
  />
  <Button
    rounded
    icon={IconZap}
    variant="outline"
    attributes={{ "aria-label": "1-click order" }}
    onClick={() => {}}
  />
  <Button
    rounded
    icon={IconZap}
    variant="ghost"
    attributes={{ "aria-label": "1-click order" }}
    onClick={() => {}}
  />
</View>

You can use the elevated property with Button when you want to add a shadow to it. Adding an elevated flag to outline buttons also adds a background color to them. Note that the elevated property is not supported for the ghost variant.

<View gap={3} direction="row">
  <Button variant="outline" elevated icon={IconZap} onClick={() => {}}>
    Elevated outline
  </Button>
  <Button color="media" elevated icon={IconZap} onClick={() => {}}>
    Elevated media
  </Button>
</View>

You can group multiple buttons together using the Button.Group compound component. Each individual button inside still works with all supported properties.

<View gap={4} align="start">
  <Button.Group>
    <Button variant="outline">File</Button>
    <Button variant="outline">Edit</Button>
    <Button variant="outline">View</Button>
  </Button.Group>

  <Button.Group>
    <Button>Save changes</Button>
    <DropdownMenu>
      <DropdownMenu.Trigger>
        {(attributes) => <Button icon={IconDown} attributes={attributes} />}
      </DropdownMenu.Trigger>
      <DropdownMenu.Content>
        <DropdownMenu.Item>Copy link</DropdownMenu.Item>
        <DropdownMenu.Item>Update permissions</DropdownMenu.Item>
        <DropdownMenu.Item>Save to folder</DropdownMenu.Item>
      </DropdownMenu.Content>
    </DropdownMenu>
  </Button.Group>
</View>

As you may have noticed, we're using a View component in the examples to group the buttons. Using ghost buttons may cause misalignment with the content since their background is not visible.

In this case, you can use the Button.Aligner utility, which will adjust the space automatically. Passing a position property to it will define which sides of the Button need to be aligned with the content. It supports both single string and array values.

<View gap={3}>
  React components that make your product shine.
  <Button.Aligner position={["start", "top"]}>
    <Button variant="ghost" color="primary" onClick={() => {}}>
      Learn more
    </Button>
  </Button.Aligner>
</View>

You can use this approach for adding icon-only buttons to your components without having them take extra space. We're also not passing the position value to the Button.Aligner component, so it applies alignment on all sides.

<Card>
  <View gap={3} direction="row">
    <View.Item grow>Content</View.Item>
    <Button.Aligner>
      <Button
        icon={IconZap}
        variant="ghost"
        attributes={{ "aria-label": "1-click order" }}
        onClick={() => {}}
      />
    </Button.Aligner>
  </View>
</Card>

When nesting the Button component inside another component rendering a button tag, you can use the as property to render it as another HTML element, like span. When used, Button will automatically add the correct role and tabIndex to it, keeping the markup valid while still making it interactive. In this case, it's also a good idea to add event.stopPropagation() to the inner button if you want to prevent click events from bubbling and triggering the onClick of its parent component.

<MenuItem
  onClick={() => {}}
  endSlot={
    <Button
      variant="ghost"
      size="small"
      icon={IconZap}
      as="span"
      onClick={(e) => {
        e.stopPropagation();
      }}
    />
  }
>
  Outer button
</MenuItem>

Button supports responsive syntax for the size and fullWidth properties. Use object syntax to control their values based on the viewport size. Responsive properties are mobile-first, so selecting a value for a viewport will also apply it to all wider viewports.

<Button
  size={{ s: "large", m: "medium" }}
  color="primary"
  fullWidth={{ s: true, m: false }}
>
  Confirm
</Button>
  • Ensure to pass a meaningful aria-label to the component when used without any visually displayed text label or wrap it with a Tooltip.
  • Use the inherit color value when rendering a Button on top of colored backgrounds to maintain a sufficient contrast ratio.
  • The <button> click event gets triggered on both Space and Enter key presses.
  • The <a> click event gets triggered on Enter key press.