Skip to content

Command

A neo-brutalist style command palette component for search and navigation. Built on reka-ui's Listbox primitive with built-in search filtering.

Demo

Preview

基础用法

未找到结果。

建议
日历
搜索表情
计算器
设置
个人资料 ⌘P
账单 ⌘B
设置 ⌘S

命令对话框

程序化控制(filterSearch)

通过 ref 写入 filterSearch 可在不操作输入框的情况下触发过滤。

未找到结果。

建议
日历
搜索表情
计算器
设置
个人资料
设置

Installation

pnpm dlx brutx-vue@latest add command

Usage

vue
<script setup>
import { Command, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem, CommandSeparator, CommandShortcut } from 'brutx-ui-vue'
</script>

<template>
    <Command>
        <CommandInput placeholder="Type a command or search..." />
        <CommandList>
            <CommandEmpty>No results found.</CommandEmpty>
            <CommandGroup title="Suggestions">
                <CommandItem value="calendar">Calendar</CommandItem>
                <CommandItem value="search">Search Emoji</CommandItem>
                <CommandItem value="calculator">Calculator</CommandItem>
            </CommandGroup>
            <CommandSeparator />
            <CommandGroup title="Settings">
                <CommandItem value="profile">
                    Profile
                    <CommandShortcut>⌘P</CommandShortcut>
                </CommandItem>
                <CommandItem value="billing">
                    Billing
                    <CommandShortcut>⌘B</CommandShortcut>
                </CommandItem>
                <CommandItem value="settings">
                    Settings
                    <CommandShortcut>⌘S</CommandShortcut>
                </CommandItem>
            </CommandGroup>
        </CommandList>
    </Command>
</template>

Search Filtering

Text entered in CommandInput automatically filters CommandItem elements. The matching logic is based on item text content. When all items are filtered out, CommandEmpty is automatically displayed; when all items within a group are filtered out, that group is automatically hidden.

vue
<Command>
    <CommandInput />
    <CommandList>
        <CommandEmpty />
        <CommandGroup title="Suggestions">
            <CommandItem value="calendar">Calendar</CommandItem>
            <CommandItem value="search">Search Emoji</CommandItem>
        </CommandGroup>
        <CommandGroup title="Settings">
            <CommandItem value="profile">Profile</CommandItem>
        </CommandGroup>
    </CommandList>
</Command>

Disable Internal Filtering

When an external component handles filtering logic (e.g., Combobox), use disable-filter to disable Command's internal search filtering to avoid double-filtering conflicts:

vue
<Command disable-filter>
    <CommandInput v-model="searchQuery" />
    <CommandList>
        <CommandEmpty />
        <CommandGroup>
            <CommandItem
                v-for="item in filteredItems"
                :key="item.value"
                :value="item.value"
            >
                {{ item.label }}
            </CommandItem>
        </CommandGroup>
    </CommandList>
</Command>

Search Widget Recipe

New code can compose Command disable-filter directly for search input, grouped results, recent searches, and loading states.

vue
<script setup>
import { computed, ref } from 'vue'
import {
    Command,
    CommandInput,
    CommandList,
    CommandEmpty,
    CommandGroup,
    CommandItem,
} from 'brutx-ui-vue'

const query = ref('')
const suggestions = [
    { label: 'Button', value: 'button', group: 'Components' },
    { label: 'Theming', value: 'theming', group: 'Docs' },
]

const filteredItems = computed(() =>
    suggestions.filter(item => item.label.toLowerCase().includes(query.value.toLowerCase()))
)
</script>

<template>
    <Command disable-filter class="w-full max-w-lg border-3 border-brutal shadow-brutal">
        <CommandInput v-model="query" placeholder="Search components or docs..." />
        <CommandList>
            <CommandEmpty />
            <CommandGroup title="Search Results">
                <CommandItem v-for="item in filteredItems" :key="item.value" :value="item.value">
                    {{ item.label }}
                </CommandItem>
            </CommandGroup>
        </CommandList>
    </Command>
</template>

Command Dialog

Use CommandDialog to create a modal command palette:

vue
<script setup>
import { ref } from 'vue'
import { CommandDialog, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem } from 'brutx-ui-vue'

const open = ref(false)
</script>

<template>
    <button @click="open = true">Open Command Palette</button>
    <CommandDialog v-model:open="open">
        <CommandInput placeholder="Type a command..." />
        <CommandList>
            <CommandEmpty />
            <CommandGroup title="Actions">
                <CommandItem value="new">New File</CommandItem>
                <CommandItem value="open">Open File</CommandItem>
            </CommandGroup>
        </CommandList>
    </CommandDialog>
</template>

Sub-components

ComponentDescription
CommandRoot container, manages filter state, built on ListboxRoot
CommandDialogModal dialog wrapper, built on DialogRoot
CommandInputSearch input field, automatically filters items on input
CommandListScrollable list container, built on ListboxContent
CommandEmptyDisplayed when there are no matching results
CommandGroupGrouped section with title, automatically hidden when filtered empty
CommandItemSelectable item, built on ListboxItem, supports @select event
CommandSeparatorVisual separator between groups
CommandShortcutKeyboard shortcut hint

Programmatic Control

Command exposes a filterSearch reactive reference via defineExpose, allowing parent components to programmatically read or set the search keyword, thereby triggering item filtering without relying on CommandInput.

Note: Writing to filterSearch only triggers filtering logic when internal filtering is enabled (i.e., disable-filter is not set); when disableFilter is true, internal filtering is disabled and writes will not affect item display.

vue
<script setup>
import { ref } from 'vue'
import { Command, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem } from 'brutx-ui-vue'

const commandRef = ref()
</script>

<template>
    <Command ref="commandRef">
        <CommandInput />
        <CommandList>
            <CommandEmpty />
            <CommandGroup title="Suggestions">
                <CommandItem value="calendar">Calendar</CommandItem>
                <CommandItem value="search">Search Emoji</CommandItem>
                <CommandItem value="calculator">Calculator</CommandItem>
            </CommandGroup>
        </CommandList>
    </Command>

    <button @click="commandRef?.filterSearch = 'cal'">Trigger search "cal" externally</button>
    <button @click="commandRef?.filterSearch = ''">Clear search</button>
</template>

Exposed API

Method/PropertyTypeDescription
filterSearchRef<string>Current search keyword, readable and writable; writing triggers internal filtering logic (requires disableFilter to be false)

Props

Command

PropTypeDefaultDescription
disableFilterbooleanfalseDisables internal search filtering, for scenarios where external filtering is used
classstringCustom CSS class name

CommandInput

PropTypeDefaultDescription
modelValuestringInput field value, supports v-model
placeholderstringt('command.placeholder')Placeholder text
classstringCustom CSS class name

CommandItem

PropTypeDefaultDescription
valuestringUnique identifier value for the item
disabledbooleanWhether the item is disabled
classstringCustom CSS class name

CommandGroup

PropTypeDefaultDescription
titlestringGroup title text
classstringCustom CSS class name

CommandList

PropTypeDefaultDescription
classstringCustom CSS class name

CommandEmpty

PropTypeDefaultDescription
classstringCustom CSS class name

CommandSeparator

PropTypeDefaultDescription
classstringCustom CSS class name

CommandShortcut

PropTypeDefaultDescription
classstringCustom CSS class name

CommandDialog

PropTypeDefaultDescription
openbooleanfalseWhether the dialog is open, supports v-model:open
titlestringt('command.dialogTitle')Dialog title (for accessibility)
descriptionstringt('command.dialogDescription')Dialog description (for accessibility)
classstringCustom CSS class name

Events

CommandInput

EventPayloadDescription
update:modelValuestringTriggered when the input value changes

CommandItem

EventPayloadDescription
selectstringTriggered when an item is selected

CommandDialog

EventPayloadDescription
update:openbooleanTriggered when the dialog open/close state changes

Slots

ComponentSlotDescription
CommanddefaultFor placing CommandInput, CommandList and other child components
CommandDialogdefaultFor placing CommandInput, CommandList and other child components
CommandListdefaultFor placing CommandEmpty, CommandGroup and other child components
CommandGroupdefaultFor placing CommandItem child components
CommandItemdefaultFor placing item content and CommandShortcut
CommandEmptydefaultCustom display content when there are no matching results, defaults to t('command.emptyText')
CommandShortcutdefaultFor placing shortcut key text

Accessibility

  • Keyboard: Supports / to move focus up and down, Enter to select the current item, Escape to close the dialog (when inside CommandDialog)
  • ARIA Attributes: CommandDialog supports title and description props for accessibility
  • Focus Management: Focus is trapped within the component when the dialog is open; focus is restored when closed

Brute force builds.