dotUI
dotUI
beta
  1. Components
  2. Buttons
  3. Button

Button

A button allows a user to perform an action, with mouse, touch, and keyboard interactions.

<Button>button</Button>

Installation

Install the following dependencies:

npm install react-aria-components

Copy and paste the following code into your project.

"use client";

import * as React from "react";
import {
  composeRenderProps,
  Button as AriaButton,
  Link as AriaLink,
  type ButtonProps as AriaButtonProps,
  type LinkProps as AriaLinkProps,
} from "react-aria-components";
import { tv, type VariantProps } from "tailwind-variants";
import { LoaderIcon } from "@/lib/icons";
import { focusRing } from "@/lib/utils/styles";

const buttonStyles = tv(
  {
    extend: focusRing,
    base: "inline-flex gap-2 cursor-pointer items-center justify-center whitespace-nowrap rounded-md leading-normal text-sm shrink-0 font-medium ring-offset-background transition-colors disabled:cursor-default disabled:bg-bg-disabled disabled:text-fg-disabled",
    variants: {
      variant: {
        default:
          "bg-bg-neutral hover:bg-bg-neutral-hover pressed:bg-bg-neutral-active text-fg-onNeutral",
        primary:
          "bg-bg-primary hover:bg-bg-primary-hover pressed:bg-bg-primary-active text-fg-onPrimary",
        quiet: "bg-transparent hover:bg-bg-inverse/10 pressed:bg-bg-inverse/20 text-fg",
        outline:
          "border border-border-field bg-transparent hover:bg-bg-inverse/10 pressed:bg-bg-inverse/20 text-fg disabled:border-border-disabled disabled:bg-transparent",
        accent:
          "bg-bg-accent hover:bg-bg-accent-hover pressed:bg-bg-accent-active text-fg-onAccent",
        success:
          "bg-bg-success hover:bg-bg-success-hover pressed:bg-bg-success-active text-fg-onSuccess",
        warning:
          "bg-bg-warning hover:bg-bg-warning-hover pressed:bg-bg-warning-active text-fg-onWarning",
        danger:
          "bg-bg-danger hover:bg-bg-danger-hover pressed:bg-bg-danger-active text-fg-onDanger",
      },
      size: {
        sm: "h-8 px-3 [&_svg]:size-4",
        md: "h-9 px-4 [&_svg]:size-4",
        lg: "h-10 px-5 [&_svg]:size-5",
      },
      shape: {
        rectangle: "",
        square: "",
        circle: "rounded-full",
      },
    },
    compoundVariants: [
      {
        size: "sm",
        shape: ["square", "circle"],
        className: "w-8 px-0",
      },
      {
        size: "md",
        shape: ["square", "circle"],
        className: "w-9 px-0",
      },
      {
        size: "lg",
        shape: ["square", "circle"],
        className: "w-10 px-0",
      },
    ],
    defaultVariants: {
      variant: "default",
      size: "md",
      shape: "rectangle",
    },
  },
  {
    responsiveVariants: ["sm", "lg"],
  }
);

interface ButtonProps
  extends Omit<AriaButtonProps, "className">,
    Omit<AriaLinkProps, "className" | "children" | "style">,
    VariantProps<typeof buttonStyles> {
  className?: string;
  isLoading?: boolean;
  prefix?: React.ReactNode;
  suffix?: React.ReactNode;
}

const Button = React.forwardRef(
  (localProps: ButtonProps, ref: React.ForwardedRef<HTMLButtonElement>) => {
    const contextProps = useButtonContext();
    const props = { ...contextProps, ...localProps };
    const { className, variant, size, shape, isDisabled, isLoading, prefix, suffix, ...restProps } =
      props;
    const Element: React.ElementType = props.href ? AriaLink : AriaButton;
    return (
      <Element
        ref={ref}
        {...restProps}
        isDisabled={isDisabled || isLoading}
        className={buttonStyles({ variant, size, shape, className })}
      >
        {composeRenderProps(props.children, (children) => (
          <>
            {isLoading ? <LoaderIcon aria-label="loading" className="animate-spin" /> : prefix}
            {typeof children === "string" ? <span className="truncate">{children}</span> : children}
            {suffix}
          </>
        ))}
      </Element>
    );
  }
);
Button.displayName = "Button";

type ButtonContextValue = VariantProps<typeof buttonStyles>;
const ButtonContext = React.createContext<ButtonContextValue>({});
const useButtonContext = () => {
  return React.useContext(ButtonContext);
};

export type { ButtonProps };
export { Button, buttonStyles, ButtonContext };

Update the import paths to match your project setup.

Usage

Button allow users to initiate an action or command with mouse, touch or keyboard interaction.
The button's label indicates the purpose of the action to the user. You may also include an icon for additional context.

Options

Variant

Use the variant prop to control the visual style of the button.
The default variant is "default".

<Button variant="default">default</Button>
<Button variant="primary">primary</Button>
<Button variant="outline">outline</Button>
<Button variant="quiet">quiet</Button>
<Button variant="success">success</Button>
<Button variant="warning">warning</Button>
<Button variant="danger">danger</Button>
<Button variant="accent">accent</Button>

Size

Use the size prop to control the size of the button.
The default size is "md".

<Button size="sm">Button</Button>
<Button size="md">Button</Button>
<Button size="lg">Button</Button>

Shape

Use the shape prop to control the shape of the button. The default variant is "rectangle".
Icon-only buttons should include an aria-label.

<Button shape="square"><UplaodIcon /></UplaodIcon>
<Button shape="circular"><UplaodIcon /></Button>

Prefix and suffix

To add additional context for a button label, such as a search icon next to the label for a search field submit, use the prefix and suffix props. Leading visuals always appear locked to the button label, even if the button is full width.

<Button prefix={<UplaodIcon />}>Upload</Button>
<Button suffix={<UplaodIcon />}>Upload</Button>

Loading

Use the isLoading prop to control the loading state of the button, if you need to wait for a button's action to be completed.

<Button isLoading>Button</Button>

Disabled

Use the isDisabled prop to disable the button.

<Button isDisabled>Button</Button>

To use a button as a link, use the href prop the button will automatically render a link that visually looks like a button.

API Reference

PropTypeDefaultDescription
variant
"default" | "primary" | "quiet" | "outline" | "accent" | "danger" | "success" | "warning"
"default"
The visual style of the button.
size
"sm" | "md" | "lg"
"md"
The size of the button.
shape
"rectangle" | "square" | "circle"
"rectangle"
The visual shape of the button.
prefix
React.ReactNode
-
A visual to display before the button text.
suffix
React.ReactNode
-
A visual to display after the button text.
isLoading
boolean
-
Whether the button is in a loading state.
isDisabled
boolean
-
Whether the button is disabled.
name
string
-
Submitted as a pair with the button's value as part of the form data.
value
string
-
The value associated with the button's name when it's submitted with the form data.
autoFocus
boolean
-
Whether the element should receive focus on render.
type
'button' | 'submit' | 'reset'
-
The behavior of the button when used in an HTML form.
href
string
-
A URL to link to.
rel
HTMLAttributeAnchorTarget
-
The relationship between the linked resource and the current page.
target
HTMLAttributeAnchorTarget
-
The target window for the link.
rel
string
-
The relationship between the linked resource and the current page.
download
boolean | string
-
Causes the browser to download the linked URL. A string may be provided to suggest a file name.
ping
string
-
A space-separated list of URLs to ping when the link is followed.
referrerPolicy
HTMLAttributeReferrerPolicy
-
How much of the referrer to send when following the link.
routerOptions
RouterOptions
-
Options for the configured client side router.
children
ReactNode | (values: ButtonRenderProps & {defaultChildren: ReactNode | undefined}) => ReactNode
-
The children of the component. A function may be provided to alter the children based on component state.
className
string
-
The CSS className for the element.
style
CSSProperties | (values: ButtonRenderProps & {defaultStyle: CSSProperties}) => CSSProperties
-
The inline style for the element. A function may be provided to compute the style based on component state.
EventTypeDescription
onPress
(e: PressEvent) => void
Handler that is called when the press is released over the target.
onPressStart
(e: PressEvent) => void
Handler that is called when a press interaction starts.
onPressEnd
(e: PressEvent) => void
Handler that is called when a press interaction ends, either over the target or when the pointer leaves the target.
onPressChange
(isPressed: boolean) => void
Handler that is called when the press state changes.
onPressUp
(e: PressEvent) => void
Handler that is called when a press is released over the target, regardless of whether it started on the target or not.
onFocus
(e: FocusEvent<Target>) => void
Handler that is called when the element receives focus.
onBlur
(e: FocusEvent<Target>) => void
Handler that is called when the element loses focus.
onFocusChange
(isFocused: boolean) => void
Handler that is called when the element's focus status changes.
onKeyDown
(e: KeyboardEvent) => void
Handler that is called when a key is pressed.
onKeyUp
(e: KeyboardEvent) => void
Handler that is called when a key is released.
onHoverStart
(e: HoverEvent) => void
Handler that is called when a hover interaction starts.
onHoverEnd
(e: HoverEvent) => void
Handler that is called when a hover interaction ends.
onHoverChange
(isHovering: boolean) => void
Handler that is called when the hover state changes.
Data attributeDescription
[data-hovered]
Whether the button is currently hovered with a mouse.
[data-pressed]
Whether the button is currently in a pressed state.
[data-focused]
Whether the button is focused, either via a mouse or keyboard.
[data-focus-visible]
Whether the button is keyboard focused.
[data-disabled]
Whether the button is disabled.

Accessibility

Keyboard interactions

KeyDescription
Space
Activates the button.
Enter
Activates the button.

Built by mehdibha. The source code is available on GitHub.