Button components

FREE

Free and open-source React button components built for modern applications and websites. These buttons are built using React Aria and styled with Tailwind CSS.

Installation

You can add this button component using our CLI or manually:

npx untitledui@latest add button

Button examples

Below are examples and variations of this button component:

We've designed our button components to be "hybrid," meaning they can function as either a link or a standard button with ease. This offers several benefits, for example, there are many situations where you might want a link that behaves like a button, or a button that acts as a link. These two essential components work interchangeably, so it was important for us to make them easy to use.

You can turn the button component into an anchor by simply passing the href prop. When the href prop is present, the button component uses the Link component from React Aria under the hood instead of Button. This means you can pass any props or attributes that an anchor tag would accept when href is provided.

Icon leading buttons

Our button component accepts the iconLeading and iconTrailing props to add an icon to the button. These icons should be either a function component that accepts a className prop or a JSX element with a data-icon attribute.

In server components, where you cannot directly pass a function component as a prop, we recommend passing a JSX element with a data-icon attribute to apply the button icon styles to the icon.

Icon trailing buttons

Similar to the iconLeading prop, you can display a trailing icon by passing a function component or a JSX element with a data-icon attribute to the iconTrailing prop.

Icon only buttons

You can also display an icon only button by omitting the button children and passing an icon to the iconLeading or iconTrailing prop.

Please note that, it's recommended to pass an aria-label to the button when using an icon only button in order to provide a better experience for screen readers as the icons themselves do not provide any context.

Loading buttons

Our button component makes it easy to implement loading states with the isLoading prop. When enabled, the button automatically displays a spinner and becomes non-interactive to prevent multiple submissions. You can control whether to show the button text alongside the spinner using the showTextWhileLoading prop—this is particularly useful for providing context about what's happening. The loading spinner is automatically sized and styled to match your button's size and color variant.

Disabled buttons

Our button component handles disabled states through the isDisabled prop, which applies appropriate styling and removes all interactive functionality. When disabled, buttons automatically change their appearance to indicate that they are not clickable, change their cursor to a not-allowed cursor, and any associated actions or navigation are prevented. The disabled styling is consistent across all button variants and sizes, ensuring a cohesive user experience throughout your application while maintaining accessibility standards.

Primary buttons destructive

Our button component provides three destructive variants: primary-destructive, secondary-destructive, and tertiary-destructive. Each variant maintains the same interaction patterns as their non-destructive counterparts while applying appropriate warning colors and styling. You can use these variants just like regular buttons, and they support all the same features including icons, loading states, and disabled states, ensuring consistency across your application's destructive actions.

API reference

Button

Import Button from @/components/base/buttons/button. Renders React Aria’s Button — all of its props are supported.

Button props
PropTypeDefaultDescription
isDisabledboolean—Disables the button and shows a disabled state.
isLoadingboolean—Shows a loading spinner and disables the button.
size
"xs""sm""md""lg""xl"
"sm"The size variant of the button.
color
"primary""secondary""tertiary""link-color""link-gray""primary-destructive""secondary-destructive""tertiary-destructive""link-destructive"
"primary"The color variant of the button.
iconLeadingReactNode | FC<{ className?: string; }>—Icon component or element to show before the text.
iconTrailingReactNode | FC<{ className?: string; }>—Icon component or element to show after the text.
noTextPaddingboolean—Removes horizontal padding from the text content.
showTextWhileLoadingboolean—When true, keeps the text visible during loading state.
childrenReactNode—The button label. Omitting it while passing an icon renders a square, icon-only button.
classNamestring—The class name applied to the button.
Props from React Aria (23)
Button inherited props
PropTypeDefaultDescription
onClick((e: MouseEvent<FocusableElement>) => void)—Not recommended – use onPress instead. onClick is an alias for onPress provided for compatibility with other libraries. onPress provides additional event details for non-mouse interactions.
onPress((e: PressEvent) => void)—Handler that is called when the press is released over the target.
onPressChange((isPressed: boolean) => void)—Handler that is called when the press state changes.
onPressEnd((e: PressEvent) => void)—Handler that is called when a press interaction ends, either over the target or when the pointer leaves the target.
onPressStart((e: PressEvent) => void)—Handler that is called when a press interaction starts.
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.
onHoverChange((isHovering: boolean) => void)—Handler that is called when the hover state changes.
onHoverEnd((e: HoverEvent) => void)—Handler that is called when a hover interaction ends.
onHoverStart((e: HoverEvent) => void)—Handler that is called when a hover interaction starts.
onBlur((e: FocusEvent<Element, Element>) => void)—Handler that is called when the element loses focus.
onFocus((e: FocusEvent<Element, Element>) => void)—Handler that is called when the element receives focus.
onFocusChange((isFocused: boolean) => void)—Handler that is called when the element's focus status changes.
autoFocusboolean—Whether the element should receive focus on render.
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.
idstring—The element's unique identifier.
styleStyleOrFunction<T>—The inline style for the element. A function may be provided to compute the style based on component state.
slotstring | null—A slot name for the component. Slots allow the component to receive props from a parent component. An explicit null value indicates that the local props completely override all props received from a parent.
render(props: DetailedHTMLProps<LinkWithRequiredHref, HTMLAnchorElement> | JSX.IntrinsicElements[Fallback], renderProps: T) => ReactElement—Overrides the default DOM element with a custom render function. This allows rendering existing components with built-in styles and behaviors such as router links, animation libraries, and pre-styled components.Note: You can check if 'href' in props in order to tell whether to render an <a> element.Requirements:
  • You must render the expected element type (e.g. if <a> is expected, you cannot render a <button>).
  • Only a single root DOM element can be rendered (no fragments).
  • You must pass through props and ref to the underlying DOM element, merging with your own prop as appropriate.
aria-describedbystring—Identifies the element (or elements) that describes the object.
aria-detailsstring—Identifies the element (or elements) that provide a detailed, extended description for the object.
aria-labelstring—Defines a string value that labels the current element.
aria-labelledbystring—Identifies the element (or elements) that labels the current element.

Also accepts the global HTML attributes (dir, lang, hidden, inert, translate), all standard React DOM event handlers.

FAQs

Please refer to our frequently asked questions page for more.

Set color to "primary", "secondary", "tertiary", "primary-destructive", "secondary-destructive", "tertiary-destructive", "link-gray", "link-color" or "link-destructive". The default is "primary".

Set size to "xs", "sm", "md", "lg" or "xl". The default is "sm".

Pass a component to iconLeading or iconTrailing, for example iconLeading={Plus}, and the button renders it at size-5 in the color variant's icon color. If you pass an element instead, add data-icon to it so it gets the same styles. A button with an icon and no children becomes icon-only with square padding, so give it an aria-label.

Set isDisabled to true. The button gets opacity-50 and cursor-not-allowed and ignores presses, and a button with an href is rendered without it so it cannot navigate.

Yes. Pass an href and Button renders a React Aria Link (an <a> element) with the same styling. For inline text links, use the "link-gray", "link-color" or "link-destructive" colors.

Set isLoading to true. The button shows a spinner in place of its content and ignores presses while keeping its width. Add showTextWhileLoading to keep the label visible next to the spinner.

Button sets type="button" unless you pass a type, so it never submits a form by default. Add type="submit" to the button that should submit.