Button

Mobile Support: Full
Jump to Props

Trigger an action or event such as submitting a form, opening a dialog or making a choice.

Appearance

To set a button's appearance, use the appearance prop:

<DocsBox css={{ backgroundColor: "$grey1", padding: "$lg" }}>
  <Stack direction="horizontal" wrap="wrap">
    <Button appearance="primary">Primary</Button>
    <Button>Standard</Button>
    <Button appearance="onSurface">On Surface</Button>
    <Button appearance="ghost">Ghost</Button>
    <Button appearance="link">Link</Button>
  </Stack>
</DocsBox>

Standard Buttons

Unless it is considered to be a primary action on a page, use a standard/default button.

Should I use a primary or standard button? Consider the context of where the button is being used and whether this button's action is the "core" action the use should perform (or simply an option to take).

<Button>Action</Button>

Primary

Use this for primary form actions, such as “save” or “update”. Ideally there should only be one primary action on a page, and definitely only one in a single form.

When there is a clear action the user is expected to perform in any given context, use the Primary action button. There should only be one primary action.

<Button appearance="primary">Primary Action</Button>

On Surface

In situations where you want to use a standard button on the vitality surface background color, you can use the onSurface variant. This will change the background color of the button 1 step darker to better contrast against the background

<DocsBox
  css={{
    backgroundColor: "$neutral12",
    padding: "$lg",
    width: "100%",
  }}
>
  <DocsBox
    css={{
      backgroundColor: "$neutral2",
      padding: "$lg",
      width: "100%",
    }}
  >
    <Stack direction="horizontal">
      <Button appearance="onSurface">Action used on Surface Colour</Button>
      <Button>Do not use Standard on surface</Button>
    </Stack>
  </DocsBox>
</DocsBox>

Destructive

As another form of primary action, use destructive buttons to suggest the high severity of an action. These should be used sparingly – in cases like deleting an un-recoverable entity or any data which will be gone forever.

<Button appearance="destructive">Delete Patient</Button>

Warning

As another form of primary action, use warning buttons to suggest the moderate severity of the action. These should be used sparingly – in cases like archiving, clearing or removing a recoverable entity or any data which will be hidden from easy access after the action is performed.

<Button appearance="warning">Clear Settings</Button>

Ghost

Ghost buttons have a transparent background and utilise the primary text colour.

<Button appearance="ghost">Clear Settings</Button>

Link buttons take on the appearance of a link including underline when hovered.

<Button appearance="link">Clear Settings</Button>

Content Guidelines

Use action verbs or phrases to tell the user what will happen next.

  1. Icon: Use an icon to convey more meaning.
  2. Label: Text that indicates the action that will be performed when selecting the button.

Sizes

Although the default size should be used predominantly, a compact size is also available. Use this size intentionally when you need to save space or inside tables.

<Stack direction="horizontal">
  <Button appearance="primary" size="compact">
    Compact
  </Button>
  <Button appearance="primary">Default</Button>
</Stack>

Fit Container

Buttons can be expanded to full width to fit their parent container.

<Button shouldFitContainer appearance="primary">
  Full width Button
</Button>

Disabled State

A button should appear disabled when its action cannot be performed. For example:

  • Form Completion: A ‘Submit’ button remains disabled until all mandatory fields are filled out.
  • Form Updates: An ‘Apply Changes’ or 'Save' button is disabled until a form is altered.
  • Sequential Actions: If actions must be performed in sequence, disable buttons for later steps until the current step is complete.
  • Permissions A button should be disabled if the user does not have the necessary permissions or access rights.

Consider the following user experience enhancements:

  1. Display a button as disabled when there is a clearly defined action or condition that the user has control over to allow the process
  2. Add the required property for any FormFields that are required to be complete before enabling buttons
() => {
  const [value, setValue] = React.useState("");

  return (
    <Stack align="end">
      <FormField
        label="First Name"
        required
        value={value}
        onChange={(e) => {
          setValue(e.target.value);
        }}
        helperMessage="This Field is required to continue"
      />

      <Button
        appearance="primary"
        disabled={!value}
        tooltipContent={!value ? "Provide first name to continue" : null}
      >
        Continue
      </Button>
    </Stack>
  );
};
  1. However when there are multiple actions with dependencies beside each other it's best to keep the buttons enabled and provide feeback in the form of toast notifications or callouts to specifiy why the action didn't follow through
() => {
  const [error, setError] = React.useState("");

  return (
    <Box width="100%">
      <Stack align="end">
        {error && (
          <Callout
            severity="critical"
            title="Something went wrong"
            description={`You cannot ${error} because this is a demo`}
          />
        )}
        <Stack direction="horizontal">
          <Button onClick={() => setError("Preview")}>Preview</Button>
          <Button onClick={() => setError("Print")}>Print</Button>
          <Button onClick={() => setError("Save")} appearance="primary">
            Save
          </Button>
        </Stack>
      </Stack>
    </Box>
  );
};

Tooltips on disabled buttons

Including a tooltip for a disabled button is required. It provides clarity to users about why the button is disabled and helps manage their expectations. Add a message in in a tooltip that explains the reason for the button’s disabled state. For example:

“Insufficient permissions” This way, users will understand why they can’t interact with the button and what action they need to take to enable it.

<DocsBox css={{ backgroundColor: "$grey1", padding: "$lg" }}>
  <Stack direction="horizontal">
    <Tooltip content="Insufficient permissions">
      <Button disabled>Edit Quote</Button>
    </Tooltip>
    <Button>Edit Quote</Button>
  </Stack>
</DocsBox>

Loading State

You can show a Spinner as an overlay on the button when you set an isLoading prop to true.

The component adds a spinner with the correct size and color based on the size and appearance props passed to the button.

() => {
  const [isLoading, setIsLoading] = React.useState();
  return (
    <Stack>
      <Typography variant="sectionTitle">Loading Example</Typography>

      <Stack direction="horizontal">
        <Button isLoading={isLoading} onClick={() => setIsLoading(true)}>
          Cancel
        </Button>
        <Button
          appearance="primary"
          isLoading={isLoading}
          onClick={() => setIsLoading(true)}
        >
          Save Changes
        </Button>
        <Button
          appearance="primary"
          size="compact"
          isLoading={isLoading}
          onClick={() => setIsLoading(true)}
        >
          Save Changes
        </Button>
      </Stack>
      <Typography variant="caption">
        Note: in reality, only one button would be set to loading at any time
      </Typography>
      <Checkbox
        onChange={() => setIsLoading(!isLoading)}
        checked={isLoading}
        value={isLoading}
        label="isLoading"
        name="isLoading"
        id="isLoading"
      />
    </Stack>
  );
};

Icons

Use an icon to convey meaning quicker. Buttons can include an icon before or after the text.

Left Icon

<Button appearance="primary" iconLeft={<Medications />}>
  Add
</Button>

Right Icon

<Button appearance="primary" iconRight={<Request />}>
  New Request
</Button>

Do not include both left and right icons - choose one.

Examples

<DocsBox
  css={{
    display: "flex",
    flexDirection: "column",
    backgroundColor: "$grey1",
    padding: "$lg",
    gap: "$md",
    width: "100%",
    boxShadow: "$sm",
    borderRadius: "$default",
  }}
>
  <Stack direction="horizontal" justify="between" wrap="wrap" gap="sm">
    <Typography variant="pageTitle">Example</Typography>
    <Stack direction="horizontal">
      <Button>Wrap-up</Button>
      <Button iconLeft={<Pdf />}>Print</Button>
      <Button appearance="primary" iconLeft={<Add />}>
        Add
      </Button>
    </Stack>
  </Stack>
  <Typography>
    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam vulputate
    eros arcu, a vehicula urna sagittis in. Duis libero justo, vehicula at dui
    a, luctus maximus elit. Nulla quis orci vel diam placerat porta et sit amet
    nunc.{" "}
  </Typography>
  <Stack direction="horizontal" align="center" justify="end">
    <Typography variant="caption">Was this page helpful?</Typography>
    <Button size="compact">No</Button>
    <Button size="compact">Yes</Button>
  </Stack>
</DocsBox>
<DocsFlex
  spacing="lg"
  direction="column"
  css={{
    backgroundColor: "$grey1",
    padding: "$lg",
    minWidth: 250,
    boxShadow: "$sm",
    borderRadius: "$default",
  }}
>
  <Typography variant="sectionTitle">
    Permanently delete this patient?
  </Typography>
  <Typography>Please be aware that this action is irreversible.</Typography>
  <Stack direction="horizontal" justify="end">
    <Button>Cancel</Button>
    <Button appearance="destructive">Yes, Delete</Button>
  </Stack>
</DocsFlex>

Figma Library

Figma.logo

Props

appearance

Description

The visual appearance of the component.

Type

"primary" | "warning" | "link" | "onSurface" | "ghost" | "standard" | "destructive"

Default Value

standard

children

Description

Text contents of the component.

Type

ReactNode

iconLeft

Description

The Icon to the left of the Button text.

Type

ReactNode

iconRight

Description

The Icon to the right of the Button text.

Type

ReactNode

isLoading

Description

Show a spinner as an overlay on the button when an asynchronous process is loading. Disables the button whilst true

Type

(boolean | "true"

shouldFitContainer

Type

boolean

size

Description

The size of the button

Type

"default" | "compact"

Default Value

default

© 2025