AI
NUI logov3.1.0

Button

A polymorphic, accessible, and composable action component designed for primary interactions. Built on @nofinite/nuicss design tokens and superclasses, Button supports visual variants, size scales, loading indicators, icon slots, and framework routing via slot-based polymorphism (asChild).

tsx
import { Button } from '@nofinite/nui';

Interactive Preview


Variants

Choose from 6 visual styles to clearly express hierarchy and action intent:

VariantSuperclassPurpose
primarybtn-primaryHigh-emphasis primary call-to-action on a screen or card
defaultbtn-defaultStandard secondary action (neutral background)
outlinebtn-outlineBordered action with transparent background
ghostbtn-ghostLow-emphasis toolbar or utility action
dangerbtn-dangerDestructive actions (delete, terminate, purge)
linkbtn-linkUnderstated inline navigation action styled as a link
tsx
<div className="flex flex-wrap items-center gap-3">
  <Button variant="primary">Primary Action</Button>
  <Button variant="default">Secondary</Button>
  <Button variant="outline">Outline</Button>
  <Button variant="ghost">Ghost</Button>
  <Button variant="danger">Delete</Button>
  <Button variant="link">Learn more</Button>
</div>

Sizes

Buttons support 4 size scales to adapt across dense data tables, standard forms, and prominent hero layouts:

tsx
<div className="flex flex-wrap items-center gap-3">
  <Button size="sm" variant="primary">Small</Button>
  <Button size="md" variant="primary">Medium</Button>
  <Button size="lg" variant="primary">Large</Button>
  <Button size="icon" variant="outline" aria-label="Add bookmark">★</Button>
</div>
SizeSuperclassCommon Use Case
smbtn-smCompact table rows, dense toolbars, data grid actions
mdbtn-mdStandard dialog actions, forms, settings screens (default)
lgbtn-lgLanding page CTAs, onboarding steps, hero banners
iconbtn-iconSquare square-aspect icon action buttons (e.g. close, search)

States & Icons

Loading & Disabled

Built-in loading indicator that preserves the button's layout dimensions, prevents duplicate submissions, sets aria-disabled="true", and displays an accessible spinner:

tsx
<div className="flex flex-wrap gap-3">
  <Button loading variant="primary">Saving changes...</Button>
  <Button disabled variant="outline">Disabled</Button>
</div>
Tip

Both loading and isLoading props are supported interchangeably for developer ergonomics.

Icons (iconLeft & iconRight)

Prepend or append icons while maintaining optical alignment and spacing:

tsx
import { Button } from '@nofinite/nui';
import { ArrowLeft, ArrowRight, Download } from 'lucide-react';

export default function IconButtons() {
  return (
    <div className="flex gap-3">
      <Button iconLeft={<ArrowLeft size={16} />} variant="outline">
        Back
      </Button>
      <Button iconRight={<ArrowRight size={16} />} variant="primary">
        Next
      </Button>
      <Button iconLeft={<Download size={16} />} variant="default">
        Export CSV
      </Button>
    </div>
  );
}

Polymorphism (asChild)

Use asChild to delegate rendering to a framework link component (such as Next.js Link or React Router Link). This ensures proper client-side routing while inheriting complete button styling, focus rings, and keyboard accessibility:

tsx
import Link from 'next/link';
import { Button } from '@nofinite/nui';

export default function NavCTA() {
  return (
    <Button asChild variant="primary">
      <Link href="/dashboard">Go to Dashboard</Link>
    </Button>
  );
}

NUICSS Superclass Mapping

Under the hood, @nofinite/nui maps all variants and sizes directly to @nofinite/nuicss superclasses. If you are building pure HTML templates or headless components, you can use the exact same classes:

html
<!-- Equivalent pure CSS usage -->
<button class="btn btn-primary btn-md">Primary Button</button>
<button class="btn btn-danger btn-sm">Delete</button>
<a href="/login" class="btn btn-outline">Sign In</a>

Keyboard Interaction

KeyBehavior
EnterActivates the button
SpaceActivates the button
TabMoves focus to the button
Shift + TabMoves focus to previous focusable element

Accessibility

  • Renders a native <button> element with semantic attributes.
  • Automatically handles disabled and aria-disabled="true" when in loading or disabled states.
  • When using size="icon", always provide an explicit aria-label for screen reader announcements.
  • Slot-based polymorphism (asChild) preserves accessibility tags on underlying custom elements.

Best Practices

Do

  • Use variant="primary" for the single most prominent action in a layout or view.
  • Always provide an aria-label when rendering icon-only buttons (size="icon").
  • Set loading during async server actions to prevent duplicate submissions.
  • Use asChild when navigating between routes.

Don't

  • Do not place multiple competing primary buttons inside the same container.
  • Avoid using variant="danger" for non-destructive actions.
  • Never nest a native <button> inside an <a> tag; use asChild instead.

API Reference

PropTypeDefaultDescription
variantenumdefault—
sizeenummd—
isLoadingbooleanfalse—
loadingbooleanfalse—
iconLeftReactNode——
iconRightReactNode——
asChildbooleanfalse—