Docs Components Navigation

Command

React example follows below.
ts
import * as React from "react";
/**
* Command palette fixture. `groups` is the shared catalogue every adapter
* renders; `query` and `activeIndex` pin the filtered state the
* five-framework render proof compares (typing "de" drops the Navigate group
* entirely and leaves two Actions rows, of which the second is active).
*
* Every item carries an explicit `value`: React's search-text extractor is
* `value ?? (typeof label === "string" ? label : "")` while the other four
* use ui-core's `defaultCommandSearchText` (`value ?? label`), so pinning
* `value` makes the filter identical by construction rather than by
* coincidence.
*/
const commandFixture = {
id: "command",
trigger: "Open command palette",
placeholder: "Search commands…",
query: "de",
activeIndex: 1,
groups: [
{
heading: "Navigate",
items: [
{ label: "Go to dashboard", value: "dashboard", shortcut: "G D" },
{ label: "Go to settings", value: "settings", shortcut: "G S" },
],
},
{
heading: "Actions",
items: [
{ label: "Deploy ledger-api", value: "deploy", shortcut: "⌘ ⏎" },
{ label: "Redeploy last build", value: "redeploy" },
{ label: "Roll back release", value: "rollback", shortcut: "⌘ R" },
],
},
],
} satisfies {
id: string;
trigger: string;
placeholder: string;
query: string;
activeIndex: number;
groups: readonly {
heading: string;
items: readonly { label: string; value: string; shortcut?: string }[];
}[];
};
import { Button, Command, type CommandGroup } from "@grassroot/ui-react";
/**
* Command is a full-viewport overlay, so — like DialogExample — the canonical
* example is the closed palette behind its trigger: mounting it already open
* would blanket the catalogue page. The open/filtered state is pinned by the
* fixture (`query`, `activeIndex`) and proved in each adapter's own test.
*/
export default function CommandDemo() {
const [open, setOpen] = React.useState(false);
const groups: CommandGroup[] = commandFixture.groups.map((group) => ({
heading: group.heading,
items: group.items.map((item) => ({
label: item.label,
value: item.value,
shortcut: item.shortcut,
})),
}));
return (
<div>
<Button onClick={() => setOpen(true)}>{commandFixture.trigger}</Button>
<Command
groups={groups}
open={open}
onOpenChange={setOpen}
placeholder={commandFixture.placeholder}
/>
</div>
);
}
ts
<script setup lang="ts">
import { ref } from "vue";
/**
* Command palette fixture. `groups` is the shared catalogue every adapter
* renders; `query` and `activeIndex` pin the filtered state the
* five-framework render proof compares (typing "de" drops the Navigate group
* entirely and leaves two Actions rows, of which the second is active).
*
* Every item carries an explicit `value`: React's search-text extractor is
* `value ?? (typeof label === "string" ? label : "")` while the other four
* use ui-core's `defaultCommandSearchText` (`value ?? label`), so pinning
* `value` makes the filter identical by construction rather than by
* coincidence.
*/
const commandFixture = {
id: "command",
trigger: "Open command palette",
placeholder: "Search commands…",
query: "de",
activeIndex: 1,
groups: [
{
heading: "Navigate",
items: [
{ label: "Go to dashboard", value: "dashboard", shortcut: "G D" },
{ label: "Go to settings", value: "settings", shortcut: "G S" },
],
},
{
heading: "Actions",
items: [
{ label: "Deploy ledger-api", value: "deploy", shortcut: "⌘ ⏎" },
{ label: "Redeploy last build", value: "redeploy" },
{ label: "Roll back release", value: "rollback", shortcut: "⌘ R" },
],
},
],
} satisfies {
id: string;
trigger: string;
placeholder: string;
query: string;
activeIndex: number;
groups: readonly {
heading: string;
items: readonly { label: string; value: string; shortcut?: string }[];
}[];
};
import { Button, Command } from "@grassroot/ui-vue";
const open = ref(false);
</script>
<template>
<div>
<Button @click="open = true">{{ commandFixture.trigger }}</Button>
<Command
v-model:open="open"
:groups="commandFixture.groups"
:placeholder="commandFixture.placeholder"
/>
</div>
</template>
ts
import { Component, signal } from "@angular/core";
/**
* Command palette fixture. `groups` is the shared catalogue every adapter
* renders; `query` and `activeIndex` pin the filtered state the
* five-framework render proof compares (typing "de" drops the Navigate group
* entirely and leaves two Actions rows, of which the second is active).
*
* Every item carries an explicit `value`: React's search-text extractor is
* `value ?? (typeof label === "string" ? label : "")` while the other four
* use ui-core's `defaultCommandSearchText` (`value ?? label`), so pinning
* `value` makes the filter identical by construction rather than by
* coincidence.
*/
const commandFixture = {
id: "command",
trigger: "Open command palette",
placeholder: "Search commands…",
query: "de",
activeIndex: 1,
groups: [
{
heading: "Navigate",
items: [
{ label: "Go to dashboard", value: "dashboard", shortcut: "G D" },
{ label: "Go to settings", value: "settings", shortcut: "G S" },
],
},
{
heading: "Actions",
items: [
{ label: "Deploy ledger-api", value: "deploy", shortcut: "⌘ ⏎" },
{ label: "Redeploy last build", value: "redeploy" },
{ label: "Roll back release", value: "rollback", shortcut: "⌘ R" },
],
},
],
} satisfies {
id: string;
trigger: string;
placeholder: string;
query: string;
activeIndex: number;
groups: readonly {
heading: string;
items: readonly { label: string; value: string; shortcut?: string }[];
}[];
};
import { GrassrootButton, GrassrootCommand } from "@grassroot/ui-angular";
@Component({
selector: "app-command-demo",
standalone: true,
imports: [GrassrootButton, GrassrootCommand],
template: `
<div>
<grassroot-button (click)="open.set(true)">{{ fixture.trigger }}</grassroot-button>
<grassroot-command
[groups]="fixture.groups"
[(open)]="open"
[placeholder]="fixture.placeholder"
/>
</div>
`,
})
export class CommandDemoComponent {
protected readonly fixture = commandFixture;
protected readonly open = signal(false);
}
export default CommandDemoComponent;
ts
<script lang="ts">
import { Button, Command } from "@grassroot/ui-svelte";
/**
* Command palette fixture. `groups` is the shared catalogue every adapter
* renders; `query` and `activeIndex` pin the filtered state the
* five-framework render proof compares (typing "de" drops the Navigate group
* entirely and leaves two Actions rows, of which the second is active).
*
* Every item carries an explicit `value`: React's search-text extractor is
* `value ?? (typeof label === "string" ? label : "")` while the other four
* use ui-core's `defaultCommandSearchText` (`value ?? label`), so pinning
* `value` makes the filter identical by construction rather than by
* coincidence.
*/
const commandFixture = {
id: "command",
trigger: "Open command palette",
placeholder: "Search commands…",
query: "de",
activeIndex: 1,
groups: [
{
heading: "Navigate",
items: [
{ label: "Go to dashboard", value: "dashboard", shortcut: "G D" },
{ label: "Go to settings", value: "settings", shortcut: "G S" },
],
},
{
heading: "Actions",
items: [
{ label: "Deploy ledger-api", value: "deploy", shortcut: "⌘ ⏎" },
{ label: "Redeploy last build", value: "redeploy" },
{ label: "Roll back release", value: "rollback", shortcut: "⌘ R" },
],
},
],
} satisfies {
id: string;
trigger: string;
placeholder: string;
query: string;
activeIndex: number;
groups: readonly {
heading: string;
items: readonly { label: string; value: string; shortcut?: string }[];
}[];
};
let open = $state(false);
</script>
<div>
<Button onClick={() => (open = true)}>{commandFixture.trigger}</Button>
<Command
groups={commandFixture.groups}
{open}
onOpenChange={(next) => (open = next)}
placeholder={commandFixture.placeholder}
/>
</div>
ts
/** @jsxImportSource solid-js */
import { createSignal } from "solid-js";
import { Button, Command } from "@grassroot/ui-solid";
/**
* Command palette fixture. `groups` is the shared catalogue every adapter
* renders; `query` and `activeIndex` pin the filtered state the
* five-framework render proof compares (typing "de" drops the Navigate group
* entirely and leaves two Actions rows, of which the second is active).
*
* Every item carries an explicit `value`: React's search-text extractor is
* `value ?? (typeof label === "string" ? label : "")` while the other four
* use ui-core's `defaultCommandSearchText` (`value ?? label`), so pinning
* `value` makes the filter identical by construction rather than by
* coincidence.
*/
const commandFixture = {
id: "command",
trigger: "Open command palette",
placeholder: "Search commands…",
query: "de",
activeIndex: 1,
groups: [
{
heading: "Navigate",
items: [
{ label: "Go to dashboard", value: "dashboard", shortcut: "G D" },
{ label: "Go to settings", value: "settings", shortcut: "G S" },
],
},
{
heading: "Actions",
items: [
{ label: "Deploy ledger-api", value: "deploy", shortcut: "⌘ ⏎" },
{ label: "Redeploy last build", value: "redeploy" },
{ label: "Roll back release", value: "rollback", shortcut: "⌘ R" },
],
},
],
} satisfies {
id: string;
trigger: string;
placeholder: string;
query: string;
activeIndex: number;
groups: readonly {
heading: string;
items: readonly { label: string; value: string; shortcut?: string }[];
}[];
};
export default function CommandDemo() {
const [open, setOpen] = createSignal(false);
return (
<div>
<Button onClick={() => setOpen(true)}>{commandFixture.trigger}</Button>
<Command
groups={commandFixture.groups.map((group) => ({
heading: group.heading,
items: [...group.items],
}))}
open={open()}
onOpenChange={setOpen}
placeholder={commandFixture.placeholder}
/>
</div>
);
}

Installation

API Reference

Import

import * as React from "react";
import { Button, Command, type CommandGroup } from "@grassroot/ui-react";

Props

Prop
Type
Default
Requirement
Description
groups
CommandGroup[]
—
required
—
value
string | undefined
—
optional
—
defaultValue
string | undefined
—
optional
—
onValueChange
((value: string) => void) | undefined
—
optional
—
query
string | undefined
—
optional
—
defaultQuery
string | undefined
—
optional
—
onQueryChange
((query: string) => void) | undefined
—
optional
—
open
boolean | undefined
—
optional
—
defaultOpen
boolean | undefined
—
optional
—
onOpenChange
((open: boolean) => void) | undefined
—
optional
—
filter
((item: CommandItem, normalizedQuery: string) => boolean) | undefined
—
optional
—
dir
"ltr" | "rtl" | undefined
—
optional
—
placeholder
string | undefined
—
optional
—
hotkey
boolean | undefined
true
optional
Bind ⌘K / Ctrl-K to toggle open.
Prop
groups
Type
CommandGroup[]
Default
—
Requirement
required
Description
—
Prop
value
Type
string | undefined
Default
—
Requirement
optional
Description
—
Prop
defaultValue
Type
string | undefined
Default
—
Requirement
optional
Description
—
Prop
onValueChange
Type
((value: string) => void) | undefined
Default
—
Requirement
optional
Description
—
Prop
query
Type
string | undefined
Default
—
Requirement
optional
Description
—
Prop
defaultQuery
Type
string | undefined
Default
—
Requirement
optional
Description
—
Prop
onQueryChange
Type
((query: string) => void) | undefined
Default
—
Requirement
optional
Description
—
Prop
open
Type
boolean | undefined
Default
—
Requirement
optional
Description
—
Prop
defaultOpen
Type
boolean | undefined
Default
—
Requirement
optional
Description
—
Prop
onOpenChange
Type
((open: boolean) => void) | undefined
Default
—
Requirement
optional
Description
—
Prop
filter
Type
((item: CommandItem, normalizedQuery: string) => boolean) | undefined
Default
—
Requirement
optional
Description
—
Prop
dir
Type
"ltr" | "rtl" | undefined
Default
—
Requirement
optional
Description
—
Prop
placeholder
Type
string | undefined
Default
—
Requirement
optional
Description
—
Prop
hotkey
Type
boolean | undefined
Default
true
Requirement
optional
Description
Bind ⌘K / Ctrl-K to toggle open.

Events

EventPayload
onValueChange((value: string) => void) | undefined
onQueryChange((query: string) => void) | undefined
onOpenChange((open: boolean) => void) | undefined

Import

import { ref } from "vue";
import { Button, Command } from "@grassroot/ui-vue";

Props

Prop
Type
Default
Requirement
groups
readonly PublicCommandGroup[]
—
required
value
string
undefined
optional
defaultValue
string
undefined
optional
query
string
undefined
optional
defaultQuery
string
undefined
optional
open
boolean | undefined
undefined
optional
defaultOpen
boolean
false
optional
filter
(item: VueCommandItem["original"], query: string) => boolean
undefined
optional
dir
"ltr" | "rtl"
undefined
optional
placeholder
string
"Type a command…"
optional
hotkey
boolean
true
optional
Prop
groups
Type
readonly PublicCommandGroup[]
Default
—
Requirement
required
Prop
value
Type
string
Default
undefined
Requirement
optional
Prop
defaultValue
Type
string
Default
undefined
Requirement
optional
Prop
query
Type
string
Default
undefined
Requirement
optional
Prop
defaultQuery
Type
string
Default
undefined
Requirement
optional
Prop
open
Type
boolean | undefined
Default
undefined
Requirement
optional
Prop
defaultOpen
Type
boolean
Default
false
Requirement
optional
Prop
filter
Type
(item: VueCommandItem["original"], query: string) => boolean
Default
undefined
Requirement
optional
Prop
dir
Type
"ltr" | "rtl"
Default
undefined
Requirement
optional
Prop
placeholder
Type
string
Default
"Type a command…"
Requirement
optional
Prop
hotkey
Type
boolean
Default
true
Requirement
optional

Events

EventPayload
update:open(_open: boolean) => true
update:value(_value: string) => true
update:query(_query: string) => true

Import

import { Component, signal } from "@angular/core";
import { GrassrootButton, GrassrootCommand } from "@grassroot/ui-angular";

Props

Prop
Type
Default
Requirement
groups
readonly CoreCommandGroup<AngularCommandItem>[]
—
required
value
string
—
optional
defaultValue
string
—
optional
open
boolean
—
optional
defaultOpen
boolean
false
optional
query
string
—
optional
defaultQuery
string
—
optional
filter
CommandFilter<AngularCommandItem>
—
optional
dir
"ltr" | "rtl"
—
optional
placeholder
string
"Type a command…"
optional
hotkey
boolean
true
optional
Prop
groups
Type
readonly CoreCommandGroup<AngularCommandItem>[]
Default
—
Requirement
required
Prop
value
Type
string
Default
—
Requirement
optional
Prop
defaultValue
Type
string
Default
—
Requirement
optional
Prop
open
Type
boolean
Default
—
Requirement
optional
Prop
defaultOpen
Type
boolean
Default
false
Requirement
optional
Prop
query
Type
string
Default
—
Requirement
optional
Prop
defaultQuery
Type
string
Default
—
Requirement
optional
Prop
filter
Type
CommandFilter<AngularCommandItem>
Default
—
Requirement
optional
Prop
dir
Type
"ltr" | "rtl"
Default
—
Requirement
optional
Prop
placeholder
Type
string
Default
"Type a command…"
Requirement
optional
Prop
hotkey
Type
boolean
Default
true
Requirement
optional

Events

EventPayload
valueChangestring
openChangeboolean
queryChangestring

Import

import { Button, Command } from "@grassroot/ui-svelte";

Props

Prop
Type
Default
Requirement
groups
readonly PublicCommandGroup[]
—
required
open
boolean
—
optional
defaultOpen
boolean
false
optional
value
string
—
optional
defaultValue
string
—
optional
query
string
—
optional
defaultQuery
string
—
optional
filter
CommandMachineInput["filter"]
—
optional
dir
"ltr" | "rtl"
—
optional
placeholder
string
"Type a command…"
optional
hotkey
boolean
true
optional
onOpenChange
(open: boolean) => void
—
optional
onValueChange
(value: string) => void
—
optional
onQueryChange
(query: string) => void
—
optional
Prop
groups
Type
readonly PublicCommandGroup[]
Default
—
Requirement
required
Prop
open
Type
boolean
Default
—
Requirement
optional
Prop
defaultOpen
Type
boolean
Default
false
Requirement
optional
Prop
value
Type
string
Default
—
Requirement
optional
Prop
defaultValue
Type
string
Default
—
Requirement
optional
Prop
query
Type
string
Default
—
Requirement
optional
Prop
defaultQuery
Type
string
Default
—
Requirement
optional
Prop
filter
Type
CommandMachineInput["filter"]
Default
—
Requirement
optional
Prop
dir
Type
"ltr" | "rtl"
Default
—
Requirement
optional
Prop
placeholder
Type
string
Default
"Type a command…"
Requirement
optional
Prop
hotkey
Type
boolean
Default
true
Requirement
optional
Prop
onOpenChange
Type
(open: boolean) => void
Default
—
Requirement
optional
Prop
onValueChange
Type
(value: string) => void
Default
—
Requirement
optional
Prop
onQueryChange
Type
(query: string) => void
Default
—
Requirement
optional

Events

EventPayload
onOpenChange(open: boolean) => void
onValueChange(value: string) => void
onQueryChange(query: string) => void

Import

import { createSignal } from "solid-js";
import { Button, Command } from "@grassroot/ui-solid";

Props

Prop
Type
Default
Requirement
groups
CommandGroup[]
—
required
value
string
—
optional
defaultValue
string
—
optional
onValueChange
(value: string) => void
—
optional
query
string
—
optional
defaultQuery
string
—
optional
onQueryChange
(query: string) => void
—
optional
open
boolean
—
optional
defaultOpen
boolean
—
optional
onOpenChange
(open: boolean) => void
—
optional
filter
(item: CommandItem, normalizedQuery: string) => boolean
—
optional
dir
"ltr" | "rtl"
—
optional
placeholder
string
"Type a command…"
optional
hotkey
boolean
true
optional
Prop
groups
Type
CommandGroup[]
Default
—
Requirement
required
Prop
value
Type
string
Default
—
Requirement
optional
Prop
defaultValue
Type
string
Default
—
Requirement
optional
Prop
onValueChange
Type
(value: string) => void
Default
—
Requirement
optional
Prop
query
Type
string
Default
—
Requirement
optional
Prop
defaultQuery
Type
string
Default
—
Requirement
optional
Prop
onQueryChange
Type
(query: string) => void
Default
—
Requirement
optional
Prop
open
Type
boolean
Default
—
Requirement
optional
Prop
defaultOpen
Type
boolean
Default
—
Requirement
optional
Prop
onOpenChange
Type
(open: boolean) => void
Default
—
Requirement
optional
Prop
filter
Type
(item: CommandItem, normalizedQuery: string) => boolean
Default
—
Requirement
optional
Prop
dir
Type
"ltr" | "rtl"
Default
—
Requirement
optional
Prop
placeholder
Type
string
Default
"Type a command…"
Requirement
optional
Prop
hotkey
Type
boolean
Default
true
Requirement
optional

Events

EventPayload
onValueChange(value: string) => void
onQueryChange(query: string) => void
onOpenChange(open: boolean) => void