Skip to main content

Button

Buttons let users take actions and make choices with a single tap.

Basics

import { Button } from 'tailwind-joy/components';

The Tailwind-Joy Button behaves similarly to the native HTML <button>, so it wraps around the text displayed on its surface.

The demo below shows the three basic states available to the Button: default, disabled, and loading.

import { Box, Button } from 'tailwind-joy/components';

export function ButtonBasics() {
return (
<Box className="flex flex-wrap justify-center gap-4">
<Button>Button</Button>
<Button disabled>Disabled</Button>
<Button loading>Loading</Button>
</Box>
);
}

Disabled

Use the disabled prop to disable interaction and focus:

import { Box, Button } from 'tailwind-joy/components';

export function ButtonDisabled() {
return (
<Box className="flex flex-wrap justify-center gap-4">
<Button disabled variant="solid">
Solid
</Button>
<Button disabled variant="soft">
Soft
</Button>
<Button disabled variant="outlined">
Outlined
</Button>
<Button disabled variant="plain">
Plain
</Button>
</Box>
);
}

Loading

Add the loading prop to show the Button's loading state. The Button is disabled as long as it's loading. See Loading indicator and Loading position for customization options.

import { Box, Button } from 'tailwind-joy/components';

export function ButtonLoading() {
return (
<Box className="flex flex-wrap justify-center gap-4">
<Button loading variant="solid">
Solid
</Button>
<Button loading variant="soft">
Soft
</Button>
<Button loading variant="outlined">
Outlined
</Button>
<Button loading variant="plain">
Plain
</Button>
</Box>
);
}

Customization

Variants

The Button component supports four variants: solid (default), soft, outlined, and plain.

import { Box, Button } from 'tailwind-joy/components';

export function ButtonVariants() {
return (
<Box className="flex flex-wrap justify-center gap-4">
<Button variant="solid">Solid</Button>
<Button variant="soft">Soft</Button>
<Button variant="outlined">Outlined</Button>
<Button variant="plain">Plain</Button>
</Box>
);
}

Sizes

The Button component supports three sizes: sm, md (default), and lg.

import { Box, Button } from 'tailwind-joy/components';

export function ButtonSizes() {
return (
<Box className="flex flex-wrap items-center justify-center gap-4">
<Button size="sm">Small</Button>
<Button size="md">Medium</Button>
<Button size="lg">Large</Button>
</Box>
);
}

Colors

The Button component supports five colors: primary (default), neutral, danger, success, and warning.

import { Box, Button } from 'tailwind-joy/components';

export function ButtonColors() {
return (
<Box className="flex flex-wrap justify-center gap-4">
<Button color="primary">Primary</Button>
<Button color="neutral">Neutral</Button>
<Button color="danger">Danger</Button>
<Button color="success">Success</Button>
<Button color="warning">Warning</Button>
</Box>
);
}

Decorators

Use the startDecorator and endDecorator props to append actions and icons to either side of the Button:

import { MdAdd, MdKeyboardArrowRight } from 'react-icons/md';
import { Box, Button } from 'tailwind-joy/components';
import { iconClass } from 'tailwind-joy/utils';

export function ButtonDecorators() {
return (
<Box className="flex flex-wrap justify-center gap-4">
<Button
color="primary"
startDecorator={<MdAdd className={iconClass()} />}
>
Add to cart
</Button>
<Button
color="success"
endDecorator={<MdKeyboardArrowRight className={iconClass()} />}
>
Go to checkout
</Button>
</Box>
);
}
info

iconClass() is an adapter function provided by Tailwind-Joy.

Loading indicator

The default loading indicator uses the Circular Progress component. Use the loadingIndicator prop to replace it with a custom indicator, as shown below:

import { Box, Button } from 'tailwind-joy/components';

export function ButtonLoadingIndicator() {
return (
<Box className="flex flex-wrap justify-center gap-4">
<Button loading>Default</Button>
<Button loading loadingIndicator="Loading...">
Custom
</Button>
</Box>
);
}

Loading position

The loadingPosition prop sets the position of the Button's loading indicator. It supports three values:

  • center (default): The loading indicator is nested inside the loadingIndicatorCenter slot and replaces the Button's contents when in the loading state.
  • start: The loading indicator replaces the starting decorator when the Button is in the loading state.
  • end: The loading indicator replaces the ending decorator when the Button is in the loading state.
import { MdSend } from 'react-icons/md';
import { Box, Button } from 'tailwind-joy/components';
import { iconClass } from 'tailwind-joy/utils';

export function ButtonLoadingPosition() {
return (
<Box className="flex flex-wrap justify-center gap-4">
<Button loading loadingPosition="start">
Start
</Button>
<Button
loading
loadingPosition="end"
endDecorator={<MdSend className={iconClass()} />}
>
End
</Button>
</Box>
);
}

Buttons let users take actions, but if that action is to navigate to a new page, then an anchor tag is generally preferable over a button tag.

If you need the style of a button with the functionality of a link, then you can use the component prop to replace the default <button> with an <a>, as shown below:

overwritten style

The text color and underline that change when hovering are in the style of docusaurus.

import { MdOpenInNew } from 'react-icons/md';
import { Button } from 'tailwind-joy/components';
import { iconClass } from 'tailwind-joy/utils';

export function ButtonLink() {
return (
<Button
component="a"
href="/docs/components/button"
target="_blank"
rel="noreferrer"
color="neutral"
variant="soft"
startDecorator={<MdOpenInNew className={iconClass()} />}
>
Open in new tab
</Button>
);
}

File upload

To create a file upload button, turn the button into a label using component="label" and then create a visually-hidden input with type file.

import { MdOutlineCloudUpload } from 'react-icons/md';
import { Button } from 'tailwind-joy/components';
import { iconClass } from 'tailwind-joy/utils';

function VisuallyHiddenInput() {
return (
<input
type="file"
className="
absolute bottom-0 left-0 h-px w-px overflow-hidden
whitespace-nowrap [clip-path:inset(50%)] [clip:rect(0_0_0_0)]
"
/>
);
}

export function ButtonFileUpload() {
return (
<Button
component="label"
role={undefined}
tabIndex={-1}
color="neutral"
variant="outlined"
startDecorator={<MdOutlineCloudUpload className={iconClass()} />}
>
Upload a file
<VisuallyHiddenInput />
</Button>
);
}

Icon Button

import { IconButton } from 'tailwind-joy/components';

Use the Icon Button component for a square button to house an icon with no text content.

import { MdFavoriteBorder } from 'react-icons/md';
import { Box, IconButton } from 'tailwind-joy/components';
import { iconClass } from 'tailwind-joy/utils';

export function IconButtonBasics() {
return (
<Box className="flex flex-wrap items-center justify-center gap-4">
<IconButton variant="solid">
<MdFavoriteBorder className={iconClass()} />
</IconButton>
<IconButton variant="soft">
<MdFavoriteBorder className={iconClass()} />
</IconButton>
<IconButton variant="outlined">
<MdFavoriteBorder className={iconClass()} />
</IconButton>
<IconButton variant="plain">
<MdFavoriteBorder className={iconClass()} />
</IconButton>
</Box>
);
}

Anatomy

The Button component is composed of a single root <button> element that wraps around its contents:

<button type="button" class="tj-button-root ...">
<!-- Button contents -->
</button>

API

See the documentation below for a complete reference to all of the props available to the components mentioned here.