Button

Triggers an action — submitting a form, opening a dialog, saving a change. Use a link instead when the result is navigation.

StableSource

Import

tsx
import { Button } from "@merid/react";

Examples

Variants

primary is the single accent action on a screen. secondary (the default) is for everything else; ghost sits quietly in toolbars and dense rows; danger confirms destructive actions; link looks like inline text but keeps button semantics.

Sizes

sm is 36px, md 44px (default) and lg 50px tall. Match the size of neighbouring inputs so a row of controls shares one baseline.

With icons

leadingIcon and trailingIcon take any node. They are wrapped in aria-hidden spans, so the label alone names the button.

Loading

loading overlays a spinner, keeps the button's width so the layout does not jump, sets aria-busy and aria-disabled, and swallows clicks. The button stays focusable, so keyboard focus is not lost mid-request.

Disabled

disabled is the native attribute: the button leaves the tab order and ignores clicks. Prefer explaining why an action is unavailable over silently disabling it.

Full width

fullWidth stretches the button to its container — useful in narrow forms and on mobile.

Use asChild to render your router's link with button styling, so navigation stays a real link:

tsx
import Link from "next/link";

<Button asChild variant="primary">
  <Link href="/signup">Start free</Link>
</Button>

API reference

Button forwards its ref to the <button> and passes every other native button attribute through. type defaults to "button", so a button inside a form does not submit it unless you set type="submit".

PropTypeDefaultDescription
variant"primary" | "secondary" | "ghost" | "danger" | "link""secondary"Visual style. primary is the single accent action.
size"sm" | "md" | "lg""md"Height and padding: sm 36px, md 44px, lg 50px.
loadingbooleanfalseShows a spinner, keeps the width, sets aria-busy and blocks clicks.
leadingIconReactNode–Icon before the label. Hidden from assistive tech.
trailingIconReactNode–Icon after the label. Hidden from assistive tech.
fullWidthbooleanfalseStretches the button to the width of its container.
asChildbooleanfalseRender the single child element (e.g. a router `<Link>`) styled as this button instead of a `<button>`. Props, ref, handlers and className merge onto the child; its children are wrapped in the button's content span. `disabled`/`loading` become `aria-disabled` and block clicks. Works from server components.
type"button" | "submit" | "reset""button"Native button type.
disabledboolean–Native disabled attribute.

Styling

HookValues
.mrd-buttonRoot <button>
.mrd-button__contentWrapper around icons and label (hidden while loading)
.mrd-button__iconLeading / trailing icon slot
.mrd-button__spinnerSpinner shown while loading
data-variantprimary · secondary · ghost · danger · link
data-sizesm · md · lg
data-loadingPresent while loading
data-full-widthPresent when fullWidth

Component variables, set per size: --mrd-button-height, --mrd-button-padding, --mrd-button-font.

css
.toolbar .mrd-button {
  --mrd-button-height: 32px;
  --mrd-button-padding: 0 12px;
}

Accessibility

  • Renders a native <button>, so Enter and Space activate it and it takes part in forms.
  • The visible label is the accessible name. For icon-only actions use IconButton, which requires a label.
  • While loading, the button reports aria-busy="true" and aria-disabled="true" but keeps focus.
KeyAction
TabMoves focus to the button.
EnterActivates the button.
SpaceActivates the button.

Guidelines

Do

Use one primary button per view, labelled with a verb that says what happens: “Save changes”, “Send invite”.

Avoid

Put two primary buttons side by side, or use vague labels like “OK” and “Submit”.

Do

Use loading while a request is in flight so the button keeps its place and focus.

Avoid

Swap the label for a spinner by hand or disable the button during the request — focus is lost and the width jumps.

  • IconButton — square, icon-only actions.
  • Link — navigation rather than actions.
  • Spinner — the indicator used by loading.