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. Note that CommandDialog is a fully controlled component: you must bind v-model:open (or listen to update:open), otherwise user interactions (such as Escape or clicking the overlay) will not change the open/close state.

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, must be used inside Command
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. When a CommandInput is present, writing filterSearch externally is also reflected back into the input; if the written value differs from the input's current display, CommandInput also synchronously emits update:modelValue, keeping the parent's v-model in sync with the input display (the parent-driven v-model write-back path does not re-emit, so there is no loop).

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>

API Reference ​

Command

Command — Props
NameTypeDefaultDescription
class
Nullable
ClassValue
undefined
Class values appended to the command root container.
Details

Declared type

ClassValue

Literal values

  • false
  • null
  • true

Default value details

Resolved value
undefined

Source

Command.vue:9

packages/ui/src/components/command/Command.vue

disableFilter
boolean | undefined
false
Disables internal filtering of item text, for cases where the list is filtered externally.
Details

Declared type

boolean | undefined

Literal values

  • false
  • true

Default value details

Resolved value
false

Source

Command.vue:10

packages/ui/src/components/command/Command.vue

Command — Slots
NameSignature or typeDescription
#default
{}
Contains child components such as the command input, list, groups, and items.
Details

Declared type

{}

Source

Command.vue

packages/ui/src/components/command/Command.vue

Command — Exposes
NameSignature or typeDescription
filterSearch
string
The current shared search query. Assigning a new string updates internal matching; when disableFilter is true, unmatched items are not hidden.
Details

Declared type

string

Source

Command.vue:79

packages/ui/src/components/command/Command.vue

CommandDialog

CommandDialog — Props
NameTypeDefaultDescription
open
boolean | undefined
false
The current dialog open state. The component is fully controlled, so update it through v-model:open or by handling update:open.
Details

Declared type

boolean | undefined

Literal values

  • false
  • true

Default value details

Resolved value
false

Source

CommandDialog.vue:19

packages/ui/src/components/command/CommandDialog.vue

title
string | undefined
undefined

Runtime fallback:Command Palette

The accessible title for the command dialog. Blank or whitespace-only values fall back to the localized title.
Details

Declared type

string | undefined

Default value details

Resolved value
undefined
Runtime fallback
Command Palette

Applied by the component under the documented runtime conditions.

Source

CommandDialog.vue:20

packages/ui/src/components/command/CommandDialog.vue

description
string | undefined
undefined

Runtime fallback:Search for a command to run...

The accessible description for the command dialog. Blank or whitespace-only values fall back to the localized description.
Details

Declared type

string | undefined

Default value details

Resolved value
undefined
Runtime fallback
Search for a command to run...

Applied by the component under the documented runtime conditions.

Source

CommandDialog.vue:21

packages/ui/src/components/command/CommandDialog.vue

class
Nullable
ClassValue
undefined
Class values appended to the dialog content; structural layout classes are merged after this value.
Details

Declared type

ClassValue

Literal values

  • false
  • null
  • true

Default value details

Resolved value
undefined

Source

CommandDialog.vue:22

packages/ui/src/components/command/CommandDialog.vue

CommandDialog — Events
NameSignature or typeDescription
@update:open
[value: boolean]
Emits the new boolean when the dialog root requests an open-state change; the parent must still update open.
Details

Declared type

[value: boolean]

Literal values

  • false
  • true

Source

CommandDialog.vue

packages/ui/src/components/command/CommandDialog.vue

CommandDialog — Slots
NameSignature or typeDescription
#default
{}
Contains dialog content such as the command input, list, groups, and items.
Details

Declared type

{}

Source

CommandDialog.vue

packages/ui/src/components/command/CommandDialog.vue

CommandEmpty

CommandEmpty — Props
NameTypeDefaultDescription
class
Nullable
ClassValue
—
Class values appended to the no-results paragraph.
Details

Declared type

ClassValue

Literal values

  • false
  • null
  • true

Default value details

Not declared
—

Source

CommandEmpty.vue:9

packages/ui/src/components/command/CommandEmpty.vue

CommandEmpty — Slots
NameSignature or typeDescription
#default
{}
Custom content for the empty search state; rendered only when the internal result count is zero. Localized text is used when no slot content is provided.
  • The fallback text comes from command.emptyText: “No results found.”.
Details

Declared type

{}

Source

CommandEmpty.vue

packages/ui/src/components/command/CommandEmpty.vue

CommandGroup

CommandGroup — Props
NameTypeDefaultDescription
title
string | undefined
—
The group heading, rendered before the group items when a non-empty value is provided.
Details

Declared type

string | undefined

Default value details

Not declared
—

Source

CommandGroup.vue:9

packages/ui/src/components/command/CommandGroup.vue

class
Nullable
ClassValue
—
Class values appended to the command group root element.
Details

Declared type

ClassValue

Literal values

  • false
  • null
  • true

Default value details

Not declared
—

Source

CommandGroup.vue:10

packages/ui/src/components/command/CommandGroup.vue

CommandGroup — Slots
NameSignature or typeDescription
#default
{}
Contains the CommandItem children belonging to this group.
Details

Declared type

{}

Source

CommandGroup.vue

packages/ui/src/components/command/CommandGroup.vue

CommandInput

CommandInput — Props
NameTypeDefaultDescription
modelValue
string | undefined
undefined
The controlled command search value. When omitted, the input initially reads the shared search query from the root Command.
Details

Declared type

string | undefined

Default value details

Resolved value
undefined

Source

CommandInput.vue:12

packages/ui/src/components/command/CommandInput.vue

placeholder
string | undefined
undefined

Runtime fallback:Type a command or search...

The input placeholder. The localized default is used only for undefined or null; an empty string is kept as an explicit value.
Details

Declared type

string | undefined

Default value details

Resolved value
undefined
Runtime fallback
Type a command or search...

Applied by the component under the documented runtime conditions.

Source

CommandInput.vue:13

packages/ui/src/components/command/CommandInput.vue

class
Nullable
ClassValue
undefined
Class values appended to the native search input.
Details

Declared type

ClassValue

Literal values

  • false
  • null
  • true

Default value details

Resolved value
undefined

Source

CommandInput.vue:14

packages/ui/src/components/command/CommandInput.vue

CommandInput — Events
NameSignature or typeDescription
@update:modelValue
[value: string]
Emits the new string when the user types. It also emits the synchronized value when the shared query in the root Command changes externally.
Details

Declared type

[value: string]

Source

CommandInput.vue

packages/ui/src/components/command/CommandInput.vue

CommandItem

CommandItem — Props
NameTypeDefaultDescription
value
Required
string
—
The unique command item value. It seeds the search index, which is updated to the visible text after the default slot renders.
Details

Declared type

string

Default value details

Not declared
—

Source

CommandItem.vue:10

packages/ui/src/components/command/CommandItem.vue

disabled
boolean | undefined
—
Disables the item and passes the disabled state to the Reka UI listbox item.
Details

Declared type

boolean | undefined

Literal values

  • false
  • true

Default value details

Not declared
—

Source

CommandItem.vue:11

packages/ui/src/components/command/CommandItem.vue

class
Nullable
ClassValue
—
Class values appended to the command item root element.
Details

Declared type

ClassValue

Literal values

  • false
  • null
  • true

Default value details

Not declared
—

Source

CommandItem.vue:12

packages/ui/src/components/command/CommandItem.vue

CommandItem — Events
NameSignature or typeDescription
@select
[value: string]
Emitted with the item’s value string when the item is selected.
Details

Declared type

[value: string]

Source

CommandItem.vue

packages/ui/src/components/command/CommandItem.vue

CommandItem — Slots
NameSignature or typeDescription
#default
{}
Renders the command item content. Its rendered visible text can be used for search matching.
Details

Declared type

{}

Source

CommandItem.vue

packages/ui/src/components/command/CommandItem.vue

CommandList

CommandList — Props
NameTypeDefaultDescription
class
Nullable
ClassValue
—
Class values appended to the Reka UI listbox content element.
Details

Declared type

ClassValue

Literal values

  • false
  • null
  • true

Default value details

Not declared
—

Source

CommandList.vue:14

packages/ui/src/components/command/CommandList.vue

asChild
boolean | undefined
—
Merges the listbox content props and behavior into its single child, which becomes the rendered node.
Details

Declared type

boolean | undefined

Literal values

  • false
  • true

Default value details

Not declared
—

Source

CommandList.vue

packages/ui/src/components/command/CommandList.vue

as
AsTag | Component | undefined
—
Specifies the element or component used to render the listbox content; asChild can override it.
Details

Declared type

AsTag | Component | undefined

Literal values

  • "a"
  • "button"
  • "div"
  • "form"
  • "h2"
  • "h3"
  • "img"
  • "input"
  • "label"
  • "li"
  • "nav"
  • "ol"
  • "p"
  • "span"
  • "svg"
  • "template"
  • "ul"

Default value details

Not declared
—

Source

CommandList.vue

packages/ui/src/components/command/CommandList.vue

CommandList — Slots
NameSignature or typeDescription
#default
{}
Contains list content such as CommandEmpty and CommandGroup.
Details

Declared type

{}

Source

CommandList.vue

packages/ui/src/components/command/CommandList.vue

CommandSeparator

CommandSeparator — Props
NameTypeDefaultDescription
class
Nullable
ClassValue
—
Class values appended to the command separator root element.
Details

Declared type

ClassValue

Literal values

  • false
  • null
  • true

Default value details

Not declared
—

Source

CommandSeparator.vue:7

packages/ui/src/components/command/CommandSeparator.vue

CommandShortcut

CommandShortcut — Props
NameTypeDefaultDescription
class
Nullable
ClassValue
—
Class values appended to the shortcut hint element.
Details

Declared type

ClassValue

Literal values

  • false
  • null
  • true

Default value details

Not declared
—

Source

CommandShortcut.vue:7

packages/ui/src/components/command/CommandShortcut.vue

CommandShortcut — Slots
NameSignature or typeDescription
#default
{}
Renders the keyboard shortcut hint associated with a command item.
Details

Declared type

{}

Source

CommandShortcut.vue

packages/ui/src/components/command/CommandShortcut.vue

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.