Skip to content
v2.5.0
English
Русский
Documentation

Button

Button is the primary action component built on top of ButtonBase, with size and full-width behavior for common product actions.

Patterns

Primary Form Action

When to use:

  • A form needs one clear primary action.

Composition notes:

  • Keep the primary action visually stronger than nearby secondary actions.
  • Use type='submit' deliberately.

Trade-offs:

  • Strong action clarity.
  • Can become visually noisy if too many buttons compete for emphasis.
form-footer-actions.tsx · 44 lines
import { Button, Label, TextField, TextFieldInput } from '@vega-ui/react';
import { FormEvent, useState } from 'react';

export default function FormFooterActions() {
  const [submitted, setSubmitted] = useState('');

  const handleSubmit = (event: FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    const data = new FormData(event.currentTarget);
    setSubmitted(`${data.get('workspace')} (${data.get('slug')})`);
  };

Secondary Action Pair

When to use:

  • A local flow needs primary and secondary actions side by side.

Composition notes:

  • Use a lower-emphasis treatment for cancel or back actions.
  • Keep the button texts complementary rather than ambiguous.

Trade-offs:

  • Easy to scan.
  • Can become confusing if both buttons use the same emphasis.
action-hierarchy.tsx · 12 lines
import { Button } from '@vega-ui/react';

export default function ActionHierarchy() {
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 'var(--spacing-4)', width: '100%' }}>
      <Button type='button' fullWidth>Publish article</Button>
      <Button type='button' fullWidth variant='secondary' appearance='ghost'>Save draft</Button>
      <Button type='button' fullWidth variant='secondary' appearance='ghost'>Preview</Button>
      <Button type='button' fullWidth variant='secondary' appearance='transparent'>Discard</Button>
    </div>
  );
}

When to use:

  • Navigation needs to look like a button while staying semantically navigational.

Composition notes:

  • Use asChild with a real anchor.
  • Preserve link semantics when the interaction truly navigates.

Trade-offs:

  • Strong visual emphasis for navigation.
  • Easy to misuse when a plain link would be clearer.
navigation-with-button-visuals.tsx · 22 lines
import { Button, Icon } from '@vega-ui/react';
import { ArrowRightIcon, ExternalLinkIcon } from '@vega-ui/icons';
import './examples.css';

export default function NavigationWithButtonVisuals() {
  return (
    <div style={{ display: 'flex', flexWrap: 'wrap', gap: 'var(--spacing-4)' }}>
      <Button asChild>
        <a href='/onboarding/import'>
          Import contacts
          <Icon size='sm' className='directionalIcon'><ArrowRightIcon /></Icon>
        </a>

Comparison

Quick Decision Rule

Use Button for explicit actions initiated by the user.

Use Button when:

  • the action mutates state or submits data

Use Link when:

  • the interaction primarily navigates

Main trade-off: button is action semantics, while link is navigation semantics.

Button vs IconButton

Use Button when:

  • visible text improves clarity

Use IconButton when:

  • the action is compact and already recognizable as an icon-only control

Main trade-off: button is clearer, while icon button is denser.

Button vs ButtonBase

Use Button when:

  • product code needs a standard text action surface

Use ButtonBase when:

  • a lower-level action primitive is needed for custom composition

Choose This Component When

  • The primary job is action, not navigation.
  • Visible text helps users understand the action.
  • The UI benefits from a standard button surface.
  • the component should be the default action choice rather than a low-level primitive

Do Not Choose This Component When

  • The primary job is navigation.
  • The action is icon-only and space-constrained.
  • The interaction is really a text link in running copy.

Troubleshooting

Button Submits The Form Unexpectedly

Symptom

The button is inside a form, and clicking it submits the form.

Likely Cause

Button has no default type, so like a native <button> it is a submit button inside a form.

How To Verify

  • Inspect the rendered type.
  • Check whether the button was meant to submit the form.

Fix

  • Set type='button' when the button should not submit the form.

Polymorphic Button Behaves Strangely

Symptom

The control renders, but the child element does not support the forwarded props as expected.

Likely Cause

asChild was used with an incompatible child element.

How To Verify

  • Inspect the rendered child element.
  • Check whether it supports the intended props and semantics.

Fix

  • Use a child that matches the intended semantics, such as an anchor for navigation.
  • Keep plain button rendering when polymorphism is unnecessary.

Symptom

The control works visually, but the interaction is really navigation.

Likely Cause

Button was chosen for a navigation pattern that should keep link semantics.

How To Verify

  • Ask whether the primary job is action or navigation.
  • Compare the flow with Link or Button asChild.

Fix

  • Use Link for plain text navigation.
  • Use Button asChild with a real anchor when button visuals are still needed.