Skip to content

Actions

Button

Uppercase, letter-spaced labels on a 2px border, as on jsonresume.org. Buttons do things; links go places.

import { Button } from '@jsonresume/ui'Source(opens in a new tab)

Playground

Change the props and copy the result.

jsx
<Button variant="primary" size="md">Export PDF</Button>

Examples

Variantsprimary (red) is the single main action on a page. secondary is the everyday button. ink is a solid dark alternative for app chrome.
button/variants.tsx
import { Button } from '@jsonresume/ui';

export default function ButtonVariants() {
  return (
    <>
      <Button variant="primary">Get started</Button>
      <Button>View themes</Button>
      <Button variant="ink">Sign in</Button>
      <Button variant="ghost">Cancel</Button>
      <Button variant="danger">Delete</Button>
      <Button variant="link">Learn more</Button>
    </>
  );
}
Sizeslg matches the home page call to action.
button/sizes.tsx
import { Button } from '@jsonresume/ui';

export default function ButtonSizes() {
  return (
    <>
      <Button size="sm">Small</Button>
      <Button size="md">Medium</Button>
      <Button size="lg" variant="primary">
        Read more
      </Button>
    </>
  );
}
Loading, disabled and icon-onlyLoading keeps the label for screen readers and the width for layout.
button/states.tsx
import { Button } from '@jsonresume/ui';

export default function ButtonStates() {
  return (
    <>
      <Button variant="primary" loading>
        Publishing
      </Button>
      <Button disabled>Disabled</Button>
      <Button size="icon" aria-label="Search">
        <svg
          viewBox="0 0 16 16"
          fill="none"
          stroke="currentColor"
          strokeWidth="1.8"
          aria-hidden="true"
        >
          <circle cx="7" cy="7" r="4.5" />
          <path d="m10.5 10.5 3 3" strokeLinecap="round" />
        </svg>
      </Button>
    </>
  );
}
As a linkUse asChild when the action is navigation, so it stays a real link.
button/as-link.tsx
import { Button } from '@jsonresume/ui';

// asChild renders the child element (here a link, or a Next.js <Link>)
// with button styles, so navigation stays a real link.
export default function ButtonAsLink() {
  return (
    <Button asChild variant="primary">
      <a href="https://jsonresume.org/getting-started">Getting started</a>
    </Button>
  );
}

Usage

Do

  • Use one primary button per view, for the action most people came to take.
  • Write labels as short verbs: "Publish", "View themes".
  • Use asChild with a link for navigation.

Don’t

  • Put two primary buttons side by side.
  • Use the danger variant for anything that is not destructive.
  • Disable a button without explaining what is missing nearby.

Accessibility

  • Renders a native <button type="button"> by default, so forms are not submitted by accident.
  • Icon-only buttons need an aria-label.
  • loading sets aria-busy and disabled while keeping the accessible name.
  • Focus shows a 2px ring in the focus token, never removed.

Props

<Button>

Also accepts the attributes of the element Button renders.
PropTypeDefaultDescription
asChildbooleanfalseRender the single child (e.g. a link) with button styles instead of a <button>. loading is not available in this mode.
fullWidthboolean–Style variant.
loadingbooleanfalseShows a spinner, keeps the button's width, and disables it.
sizeiconsmmdlgmdStyle variant.
variantlinkprimaryinkdangersecondaryghostsecondaryStyle variant.