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

Manual Setup

If you prefer manual control:

Install the core dependencies
npm install clsx tailwind-merge @base-ui/react class-variance-authority lucide-react
Copy the source code
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