vuetifyjs / vuetifyjs/vuetify

[Feature Request] Add `VSplitButton` Component

Open
#22,076 4 comments 2 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

C: New Component T: feature
Dominant language
TypeScript
Stars
41k
Forks
7.1k
Avg merge
1d 21h
Merged PRs (30d)
11

Description

Problem to solve

Description

Currently, Vuetify does not provide a built-in SplitButton component. A SplitButton combines:

  • A primary action button (executes the most common/primary action).
  • A secondary dropdown toggle (reveals additional related actions).

This pattern is widely used in enterprise apps, admin panels, and productivity tools to reduce button clutter while still offering quick access to multiple related commands.


Proposed API / Usage Example
<v-split-btn
  label="Save"
  :items="[
    { label: 'Save As Draft', value: 'draft' },
    { label: 'Save & Publish', value: 'publish' }
  ]"
  color="primary"
  variant="flat"
  size="large"
  @click="onSave"
  @click:item="onSplitAction"
/>
  • Props:

    • label → primary action text
    • items → array of dropdown items (or allow slot for full flexibility)
    • color, variant, size → inherited from v-btn for consistency
  • Events:

    • primary-click → when the main button is clicked
    • item-click → when a dropdown item is selected

Why this belongs in Vuetify
  • Material Design Adjacent: Many modern design systems (Material Design, Fluent UI, Bootstrap, Ant Design) include split buttons because they solve a common UX problem: offering a default action with secondary options nearby.
  • Developer Experience: Today, developers must manually compose v-btn + v-menu + v-list. This is repetitive boilerplate and inconsistent across projects.
  • Consistency: A canonical Vuetify implementation would ensure accessibility (keyboard navigation, focus trapping, aria roles), consistent sizing/alignment, and theme support.

Alternatives Today
  • Manually compose a v-btn and a v-menu with v-list (works, but verbose and error-prone).
  • Use v-btn-toggle (not semantically the same, doesn’t support a primary action + dropdown).

References

Additional Context

This component would be especially valuable in enterprise dashboards, admin panels, and data-heavy applications, where contextual actions need to be accessible but not overwhelming.

Proposed solution

<template>
  <div class="d-inline-flex">
    <!-- Primary Action Button -->
    <v-btn
      :color="props.color"
      :variant="props.variant"
      :size="props.size"
      @click="emit('click')"
    >
      <slot>{{ props.label }}</slot>
    </v-btn>

    <!-- Dropdown Menu -->
    <v-menu>
      <template #activator="{ props: menuProps }">
        <v-btn
          v-bind="menuProps"
          :color="props.color"
          :variant="props.variant"
          :size="props.size"
          icon
        >
          <!-- Or let this icon be a prop/slot -->
          <v-icon>mdi-chevron-down</v-icon>
        </v-btn>
      </template>

      <v-list>
        <v-list-item
          v-for="(item, index) in props.items"
          :key="index"
          @click="emit('click:item', item)"
        >
          <v-list-item-title>{{ item.label }}</v-list-item-title>
        </v-list-item>
      </v-list>
    </v-menu>
  </div>
</template>

<script setup lang="ts">
// Or use a similar existing type
type SplitButtonItem = {
  label: string;
  value?: string | number | boolean | object;
};

// Or use a similar existing type
type SplitButtonVariant =
  | "text"
  | "flat"
  | "outlined"
  | "tonal"
  | "elevated"
  | "plain";

// Or use a similar existing type
type SplitButtonSize =
  | "x-small"
  | "small"
  | "default"
  | "large"
  | "x-large";

// Or use a similar existing type
type SplitButtonProps = {
  label?: string;
  items?: SplitButtonItem[];
  color?: string;
  variant?: SplitButtonVariant;
  size?: SplitButtonSize;
};

// Or use a similar existing type
type SplitButtonEmits = {
  (e: "click"): void;
  (e: "click:item", item: SplitButtonItem): void;
};

const props = withDefaults(defineProps<SplitButtonProps>(), {
  label: "Action",
  items: () => [],
  color: "primary",
  variant: "flat",
  size: "default",
});

const emit = defineEmits<SplitButtonEmits>();
</script>

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reviewing the existing v-btn, v-menu, and v-list components referenced in the proposed composition, along with their APIs and interaction patterns. Define the component's final props, events, accessibility behavior, and item handling; done means a documented VSplitButton implementation with coverage for primary and dropdown actions.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
frontend, web-dev
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.