Button
Primary action button to trigger an operation.
CLI Usage
pnpm dlx @somaui/cli add button
generates components/button and other dependent files/folder:
import React, { forwardRef, type ElementType } from 'react';
import { tv, type VariantProps } from 'tailwind-variants';
import { Loader } from '@/components/loader';
const button = tv({
base: 'font-medium inline-flex items-center justify-center cursor-pointer active:enabled:translate-y-px focus:outline-none focus-visible:ring-[1.8px] focus-visible:ring-offset-2 ring-offset-background transition-colors duration-200 rounded-[var(--border-radius)]',
variants: {
variant: {
solid:
'bg-primary hover:bg-primary-dark dark:hover:bg-primary/90 focus-visible:ring-border text-primary-foreground border-[length:var(--border-width)] border-transparent dark:backdrop-blur',
outline:
'bg-transparent border-[length:var(--border-width)] border-border hover:border-primary focus-visible:ring-border hover:text-primary dark:backdrop-blur',
flat: 'bg-muted hover:bg-primary-lighter focus-visible:ring-primary-lighter hover:text-primary-dark border-[length:var(--border-width)] border-transparent backdrop-blur',
text: 'hover:text-primary focus-visible:ring-primary-lighter border-[length:var(--border-width)] border-transparent',
danger:
'bg-red hover:bg-red-dark dark:hover:bg-red/80 focus-visible:ring-red/30 text-white border-[length:var(--border-width)] border-transparent dark:backdrop-blur',
},
size: {
sm: 'px-2.5 py-1 text-xs h-8',
md: 'px-4 py-2 text-sm h-10',
lg: 'px-5 py-2 text-base h-12',
},
disabled: {
true: 'dark:hover:bg-muted/70 cursor-not-allowed border-muted bg-muted/70 text-muted-foreground hover:text-muted-foreground backdrop-blur-xl hover:border-muted hover:bg-muted/70',
},
isLoading: {
true: 'pointer-events-none relative',
},
},
defaultVariants: {
variant: 'solid',
size: 'md',
},
});
export type ButtonProps<T extends ElementType = 'button'> = VariantProps<
typeof button
> & {
as?: T;
isLoading?: boolean;
loader?: React.ReactNode;
children?: React.ReactNode;
} & Omit<React.ComponentPropsWithRef<T>, 'as' | 'className' | 'ref'> & {
className?: string;
};
export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
(
{
children,
className,
isLoading,
as = 'button',
type = 'button',
variant = 'solid',
size = 'md',
color = 'primary',
disabled,
loader,
...buttonProps
},
ref
) => {
const Component = (as || 'button') as ElementType;
return (
<Component
ref={ref}
type={Component === 'button' ? type : undefined}
disabled={disabled}
aria-disabled={disabled}
aria-busy={isLoading}
className={button({
variant,
size,
disabled,
isLoading,
className,
})}
{...buttonProps}
>
{isLoading ? (
<>
<span className="invisible opacity-0">{children}</span>
<span className="absolute inset-0 flex h-full w-full items-center justify-center">
{loader ?? <Loader size={size} className="scale-95" />}
</span>
</>
) : (
children
)}
</Component>
);
}
);
Button.displayName = 'Button';
As a package:
import { Button } from '@somaui/ui/button';
Default
The default style of Button component.
import { Button } from '@somaui/ui/button';
export default function App() {
return <Button>Button</Button>;
}
Variants
You can change the style of the Button using variant property.
import { Button } from '@somaui/ui/button';
export default function App() {
return (
<>
<Button variant="solid">Solid</Button>
<Button variant="outline">Outline</Button>
<Button variant="flat">Flat</Button>
<Button variant="text">Text</Button>
<Button variant="danger">Danger</Button>
</>
);
}
Sizes
You can change the size of the Button using size property.
import { Button } from '@somaui/ui/button';
export default function App() {
return (
<>
<Button size="sm">Button</Button>
<Button>Button</Button>
<Button size="lg">Button</Button>
</>
);
}
Loading
You can set the loading state of the Button component using isLoading property.
import { Button } from '@somaui/ui/button';
export default function App() {
return (
<>
<Button isLoading={true}>Solid</Button>
<Button isLoading={true} variant="outline">
Outline
</Button>
<Button isLoading={true} variant="flat">
Flat
</Button>
<Button isLoading={true} variant="danger">
Danger
</Button>
</>
);
}
Disabled
This is the disabled style of the Button component.
import { Button } from '@somaui/ui/button';
export default function App() {
return <Button disabled={true}>Disabled</Button>;
}
With Icon
You can set any Icon at any position.
import { Button } from '@somaui/ui/button';
import { ArrowRightIcon } from '@heroicons/react/24/outline';
export default function App() {
return (
<Button>
<span>View Details</span>{' '}
<ArrowRightIcon strokeWidth="2" className="ml-2 h-4 w-4" />
</Button>
);
}
API Reference
Button Props
Here is the API documentation of the Button component. And the rest of the props are the same as the original html button. You can use props like id, title, onClick, onFocus, onBlur etc.
| Props | Type | Description | Default |
|---|---|---|---|
| as | button or span | Render as | "button" |
| children | React.ReactNode | Accepts everything React can render | __ |
| type | ButtonTypes | Set the original HTML type of button | "button" |
| variant | ButtonVariants | Set the button variants | "solid" |
| size | ButtonSizes | Set the size of the component. "sm" is equivalent to the dense button styling. | "md" |
| isLoading | boolean | Set the loading status of button | __ |
| disabled | boolean | Disabled state of the button | __ |
| loader | React.ReactNode | Custom Loader component to show when isLoading | __ |
| className | string | Add custom classes for extra style | __ |
| ref | Ref<HTMLButtonElement> | forwardRef | __ |
| ... | ButtonHTMLAttributes or HTMLAttributes | native props like onClick, title, aria-label ... | __ |
Button Types
type ButtonTypes = 'button' | 'submit' | 'reset';
Button Variants
type ButtonVariants = 'solid' | 'outline' | 'flat' | 'text' | 'danger';
Button Sizes
type ButtonSizes = 'sm' | 'md' | 'lg';