Components

Button

Source
A pressable button with color, variant, size, icon, avatar, and loading states.
Install with the CLI
npx @vyui/cli add button

Overview

VyButton renders a pressable control on top of the headless @vyui/core Button. It supports semantic color, six variant styles, five sizes, leading/trailing icons or an avatar, a loading spinner, and block / square layouts.

Usage

Use label for simple text, or the default slot for custom content. Listen for presses with @tap; Lynx exposes tap events instead of DOM click events.

<script setup lang="ts">
import { VyButton } from '@vyui/kit'

function saveDraft() {
  // Persist the draft.
}
</script>

<template>
  <VyButton label="Save draft" @tap="saveDraft" />
</template>

Label

Use the label prop when the button text is plain. Slot content replaces the built-in label and can contain custom Lynx markup.

<VyButton label="Continue" />

<VyButton>
  <view class="flex flex-row items-center gap-2">
    <text>Continue</text>
    <text class="text-xs text-primary-100">Beta</text>
  </view>
</VyButton>

Color

Use color to select the semantic palette.

<VyButton color="primary" label="Primary" />
<VyButton color="success" label="Success" />
<VyButton color="warning" label="Warning" />
<VyButton color="error" label="Error" />
<VyButton color="neutral" label="Neutral" />

Variant

color selects the semantic palette; variant selects the fill treatment.

<VyButton color="primary" variant="solid" label="Solid" />
<VyButton color="primary" variant="outline" label="Outline" />
<VyButton color="primary" variant="subtle" label="Subtle" />
<VyButton color="neutral" variant="soft" label="Soft" />
<VyButton color="error" variant="ghost" label="Ghost" />
<VyButton color="primary" variant="link" label="Link" />

Sizes

Use size to change padding, text scale, icon size, and avatar size.

<VyButton size="xs" label="XS" />
<VyButton size="sm" label="SM" />
<VyButton size="md" label="MD" />
<VyButton size="lg" label="LG" />
<VyButton size="xl" label="XL" />

Icon

Use icon, leadingIcon, or trailingIcon to show an Iconify glyph inside the button.

leadingIcon / trailingIcon place explicit Iconify glyphs. The icon shorthand goes to the trailing side when trailing is set, otherwise the leading side. avatar renders a VyAvatar in the leading slot instead of an icon.

<VyButton icon="i-lucide-rocket" label="Launch" />
<VyButton leading-icon="i-lucide-arrow-left" label="Back" />
<VyButton trailing-icon="i-lucide-arrow-right" label="Next" />

With no label and no default slot, the button automatically uses a square icon-only footprint. Add accessibility-label for icon-only actions.

<VyButton icon="i-lucide-search" accessibility-label="Search" />

Avatar

Use avatar to render a VyAvatar on the leading side. The avatar inherits a size token from the button unless avatar.size is set.

<VyButton
  :avatar="{ src: 'https://github.com/nuxt.png', alt: 'Nuxt' }"
  label="Nuxt"
  color="neutral"
  variant="outline"
/>

An avatar-only button also collapses to the square footprint.

<VyButton
  :avatar="{ src: 'https://github.com/nuxt.png', alt: 'Nuxt' }"
  accessibility-label="Open Nuxt profile"
/>

Loading

Use loading to show the loading icon and block interaction. Because loading sets the underlying core button to disabled, tap will not emit while it is loading.

<VyButton :loading="submitting" label="Saving" />

Use loadingIcon to override the spinner for one button. The global default comes from appConfig.ui.icons.loading.

<VyButton loading loading-icon="i-lucide-loader" label="Loading" />

Override the global loading icon through the VyUI plugin config.

provideVyUI(app, {
  ui: {
    icons: {
      loading: 'tabler:loader-2'
    }
  }
})

Disabled

Use disabled to prevent interaction and apply the disabled visual state. Loading buttons are disabled automatically.

<VyButton disabled label="Disabled" />

Form submission

VyButton does not submit a native HTML form on Lynx. With VyForm, call the exposed submit() method from @tap and bind the form slot's submitting state to loading.

<script setup lang="ts">
import { ref } from 'vue'
import { VyButton, VyForm, VyFormField, VyInput } from '@vyui/kit'

const form = ref()

function onSubmit(values: Record<string, unknown>) {
  // Send values.
}
</script>

<template>
  <VyForm ref="form" :default-values="{ email: '' }" @submit="onSubmit">
    <template #default="{ submitting }">
      <VyFormField name="email" label="Email">
        <VyInput name="email" />
      </VyFormField>

      <VyButton :loading="submitting" label="Submit" @tap="form.submit()" />
    </template>
  </VyForm>
</template>

Block and square

Use block for full-width actions. A block button with a trailing icon pushes that icon to the far edge.

<VyButton block label="Create project" trailing-icon="i-lucide-arrow-right" />

Use square to force equal-sided padding, even when content would normally create a wider button.

<VyButton square icon="i-lucide-plus" accessibility-label="Create" />

Custom slots

Use the default slot to replace the built-in label. Use leading and trailing to replace the built-in icon areas; both scoped slots receive iconColor so custom icons can match the resolved variant foreground.

<VyButton label="Deploy" variant="outline">
  <template #leading="{ iconColor }">
    <VyIcon name="i-lucide-cloud-upload" :color="iconColor" class="size-5" />
  </template>
</VyButton>

Examples

class prop

Use class to override root styles for one button.

<VyButton class="rounded-full px-5" label="Rounded" />

ui prop

Use ui to override a named theme slot after variants are resolved.

<VyButton
  icon="i-lucide-rocket"
  color="neutral"
  variant="outline"
  label="Launch"
  :ui="{
    label: 'text-primary-600 font-semibold',
    leadingIcon: 'size-6'
  }"
/>

Features and behavior

  • label renders the text; the default slot overrides it.
  • loading shows a spinner (loadingIcon, default appConfig.ui.icons.loading) and blocks interaction.
  • disabled blocks tap emission and applies the disabled visual state.
  • block stretches to full width and pushes a trailing icon to the far edge; square produces an equal-sided icon button.
  • With no label and no default slot, icon-only and avatar-only buttons automatically use the square footprint.
  • The leading / trailing slots receive iconColor so custom glyphs can match the variant's resolved foreground.
  • type and autofocus are kept for API parity with Nuxt UI v4 but are no-ops on Lynx, where the core button renders as a <view>.
  • Unlike Nuxt UI's web button, VyButton does not currently render links and does not support to, href, active variants, or automatic promise-based loading.

Props

PropTypeDefaultDescription
colorColor'primary'Semantic palette.
variant'solid' | 'outline' | 'subtle' | 'soft' | 'ghost' | 'link''solid'Fill treatment.
size'xs' | 'sm' | 'md' | 'lg' | 'xl''md'Padding and text scale.
labelstringundefinedText label; overridden by the default slot.
blockbooleanfalseStretch to full width.
squarebooleanfalseEqual-sided icon button.
disabledbooleanfalsePrevent interaction and dim the button.
loadingbooleanfalseShow a spinner and block interaction.
loadingIconstringappConfig.ui.icons.loadingSpinner Iconify name.
leadingIconstringundefinedExplicit leading Iconify name.
trailingIconstringundefinedExplicit trailing Iconify name.
iconstringundefinedIconify shorthand; routed by leading / trailing.
leadingbooleanfalseForce icon onto the leading side.
trailingbooleanfalseForce icon onto the trailing side.
avatarAvatarPropsundefinedRender a VyAvatar in the leading slot.
type'button' | 'submit' | 'reset''button'API parity only; no native form behavior on Lynx.
autofocusbooleanfalseParity only; no-op on Lynx.
classanyundefinedClasses merged onto the root.
uiPartial<Record<ButtonSlot, any>>undefinedPer-instance theme slot overrides.

Color defaults to primary, secondary, success, info, warning, error, or neutral, and supports registry extensions.

Emits

EventPayloadDescription
tapThe button was pressed. Forwarded from the underlying core Button.

Slots

SlotPropsDescription
defaultButton content; overrides label.
leading{ iconColor: string }Replaces the leading icon/avatar.
trailing{ iconColor: string }Replaces the trailing icon.

Styling and theming

Override globally through appConfig.ui.button or locally with ui.

UI slotPurpose
baseRoot layout, radius, padding, and transition.
labelTruncating text label.
leadingIconLeading icon sizing.
leadingAvatarLeading avatar wrapper.
leadingAvatarSizeAvatar size token per button size.
trailingIconTrailing icon sizing.

The variant × color matrix resolves fills and foregrounds (e.g. solid paints bg-{color}-500 with white text). Defaults are primary, solid, and md.

Accessibility

The core button exposes button semantics and announces disabled state through the Lynx accessibility helpers. Provide a clear visible label or custom accessible content; for icon-only and avatar-only buttons, pass accessibility-label.

type and autofocus are no-ops on Lynx. The control does not participate in native HTML form submission, so call the form submit handler from @tap when using VyButton with VyForm.

Platform notes

  • Interaction uses Lynx tap, exposed as Vue's @tap, rather than DOM click.
  • Lynx SVG does not inherit currentColor; built-in icons resolve the semantic foreground to an explicit SVG color.
  • Foreground classes live on label, leadingIcon, and trailingIcon, not only on base, because CSS inheritance is disabled in the Lynx build.
  • Hover and focus-visible states from the Nuxt UI web button are intentionally omitted; pressed feedback uses Lynx active: classes.
  • Icon for the leading/trailing glyphs.
  • Avatar for the avatar prop.
  • Form for submit buttons inside a form.
  • Island Button for actions inside island layouts.