MC Button

A clickable interactive element.

Overview

The mc-button is a core component of the MicroClub UI library. Built on the @base-ui/react button primitive, it combines world-class accessibility with MicroClub's signature styling via Tailwind CSS v4.

As a part of the MicroClub library, this component is designed for instant deployment into your codebase, giving you full ownership of its look and behavior.

Preview

Loading...
import { McButton } from '../ui/mc-button';export default function McButtonDemo() {  return <McButton>Button</McButton>;}

Add to Your Project

Deploy the mc-button directly to your components/ui directory:

npx mcoli-ui@latest add mc-button
pnpm dlx mcoli-ui@latest add mc-button
yarn dlx mcoli-ui@latest add mc-button
bunx mcoli-ui@latest add mc-button

Manual Setup

If you prefer manual control:

Install the core dependencies

npm install clsx tailwind-merge @base-ui/react class-variance-authority lucide-react

pnpm add clsx tailwind-merge @base-ui/react class-variance-authority lucide-react

yarn add clsx tailwind-merge @base-ui/react class-variance-authority lucide-react

bun add clsx tailwind-merge @base-ui/react class-variance-authority lucide-react

Copy the source code
'use client';import * as React from 'react';import { Button as ButtonPrimitive } from '@base-ui/react/button';import { cva, type VariantProps } from 'class-variance-authority';import { LinkIcon, Loader2 } from 'lucide-react';import { cn } from '@/lib/utils';const buttonVariants = cva(  'group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding whitespace-nowrap transition-all outline-none select-none disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0',  {    variants: {      variant: {        primary:          'bg-primary text-primary-foreground hover:bg-secondary-foreground hover:text-secondary active:bg-secondary-foreground active:text-secondary active:ring-4 active:ring-secondary disabled:bg-muted disabled:text-muted-foreground',        secondary:          'bg-secondary text-secondary-foreground hover:bg-accent hover:text-accent-foreground active:bg-accent active:text-accent-foreground active:ring-4 active:ring-secondary disabled:bg-muted disabled:text-muted-foreground',        tertiary:          'bg-primary-foreground text-primary hover:text-accent-foreground active:text-accent-foreground active:ring-4 active:ring-secondary disabled:bg-muted disabled:text-muted-foreground',        link: 'bg-transparent p-0 text-foreground underline hover:text-accent-foreground active:text-accent-foreground disabled:text-muted-foreground',      },      size: {        sm: 'px-3.5 py-2 text-sm font-medium',        md: 'px-4 py-2.5 text-sm font-medium',        lg: 'px-[1.125rem] py-2.5 text-base font-medium',        xl: 'px-5 py-3 text-base font-medium',      },      icon: {        none: '',        leading: 'gap-2',        trailing: 'gap-2',        dot: 'gap-2',        only: '',      },      destructive: {        true: '',        false: '',      },    },    compoundVariants: [      {        variant: 'link',        className: 'px-0 py-0',      },      {        icon: 'only',        size: 'sm',        className: 'p-2',      },      {        icon: 'only',        size: 'md',        className: 'p-2.5',      },      {        icon: 'only',        size: 'lg',        className: 'p-3',      },      {        icon: 'only',        size: 'xl',        className: 'p-3.5',      },      // Destructive overrides      {        destructive: true,        variant: 'primary',        className:          'bg-destructive text-destructive-foreground hover:bg-destructive/90 active:bg-destructive/90 active:ring-4 active:ring-destructive/20',      },      {        destructive: true,        variant: 'secondary',        className:          'bg-destructive/10 text-destructive hover:bg-destructive/20 active:ring-4 active:ring-destructive/20',      },      {        destructive: true,        variant: 'tertiary',        className:          'text-destructive hover:bg-destructive/10 active:ring-4 active:ring-destructive/20',      },      {        destructive: true,        variant: 'link',        className: 'text-destructive hover:text-destructive/80 active:text-destructive/80',      },    ],    defaultVariants: {      variant: 'primary',      size: 'md',      icon: 'none',      destructive: false,    },  });export interface McButtonProps  extends Omit<ButtonPrimitive.Props, 'icon'>, Omit<VariantProps<typeof buttonVariants>, 'icon'> {  iconDefinition?: React.ReactNode;  icon?: 'none' | 'leading' | 'trailing' | 'dot' | 'only';  isLoading?: boolean;}function McButton({  className,  variant = 'primary',  size,  icon = 'none',  destructive,  iconDefinition,  isLoading,  children,  disabled,  ...props}: McButtonProps) {  const isLink = variant === 'link';  const effectiveIcon =    isLink && (icon === 'leading' || icon === 'trailing') && !iconDefinition ? (      <LinkIcon />    ) : (      iconDefinition    );  return (    <ButtonPrimitive      data-slot="button"      disabled={disabled || isLoading}      className={cn(        buttonVariants({          variant,          size,          icon: icon === 'none' ? 'none' : icon,          destructive,          className,        }),        isLoading && 'gap-2'      )}      {...props}    >      {isLoading ? (        <Loader2 className="size-4 animate-spin shrink-0" />      ) : (        <>          {icon === 'dot' && (            <span data-slot="dot" className="size-2.5 shrink-0 rounded-full bg-current" />          )}          {icon === 'leading' && effectiveIcon && (            <span data-slot="leading-icon" className="size-4 shrink-0 [&_svg]:size-full">              {effectiveIcon}            </span>          )}        </>      )}      {icon !== 'only' && children}      {icon === 'only' && !isLoading && effectiveIcon}      {icon === 'trailing' && !isLoading && effectiveIcon && (        <span data-slot="trailing-icon" className="size-4 shrink-0 [&_svg]:size-full">          {effectiveIcon}        </span>      )}    </ButtonPrimitive>  );}export { McButton, buttonVariants };
Adjust import paths

Ensure your @/lib/utils or equivalent classname helper is correctly imported.

Usage

import { McButton } from '@/components/ui/mc-button';

export default function Example() {
  return <McButton variant="primary">Click me</McButton>;
}

Examples

Variants

<McButton variant="primary">Primary</McButton>
<McButton variant="secondary">Secondary</McButton>
<McButton variant="tertiary">Tertiary</McButton>
<McButton variant="link">Link</McButton>

Sizes

<McButton size="sm">Small</McButton>
<McButton size="md">Medium</McButton>
<McButton size="lg">Large</McButton>
<McButton size="xl">Extra Large</McButton>

Icons Support

The mc-button features a unified icon system using the icon and iconDefinition props.

import { Mail, ArrowRight, Plus } from "lucide-react"

<McButton icon="leading" iconDefinition={<Mail />}>
  Send Email
</McButton>

<McButton icon="trailing" iconDefinition={<ArrowRight />}>
  Get Started
</McButton>

<McButton icon="dot">
  Status Online
</McButton>

<McButton icon="only" iconDefinition={<Plus />} />

States

Handle loading and destructive actions with dedicated boolean props.

<McButton isLoading>Processing...</McButton>

<McButton destructive variant="primary">
  Delete Account
</McButton>

The link variant automatically includes a LinkIcon when an icon mode is selected but no iconDefinition is provided.

<McButton variant="link" icon="leading">
  View Documentation
</McButton>

API Reference

Props

PropTypeDefaultDescription
variant"primary" | "secondary" | "tertiary" | "link""primary"The visual style of the button
size"sm" | "md" | "lg" | "xl""md"The size of the button
icon"none" | "leading" | "trailing" | "dot" | "only""none"The placement mode for icons
iconDefinitionReactNodeundefinedThe icon to render (except for "dot" mode)
destructivebooleanfalseApplies a destructive theme to the current variant
isLoadingbooleanfalseShows a spinner and disables interactions
renderReactElement | ((props: HTMLProps) => ReactElement)undefinedPolymorphic render prop from Base UI
classNamestringundefinedAdditional CSS classes

All other props from @base-ui/react/button are supported.

On this page