Command

Command opens a searchable menu of actions or destinations. Group items to give related results context, and keep open and inputValue controlled by the consuming view.

import { useState } from "react";
import { Button, Command } from "density-base-ui";
import { FileText, Settings, Users } from "lucide-react";

const commandGroups = [
  {
    id: "workspace",
    label: "Workspace",
    items: [
      {
        id: "projects",
        label: "Projects",
        icon: <FileText size={16} />,
        onSelect: () => {},
      },
      {
        id: "team",
        label: "Team",
        icon: <Users size={16} />,
        onSelect: () => {},
      },
    ],
  },
  {
    id: "settings",
    items: [
      {
        id: "settings",
        label: "Settings",
        icon: <Settings size={16} />,
        onSelect: () => {},
      },
    ],
  },
];

export default function CommandExample() {
  const [open, setOpen] = useState(false);
  const [inputValue, setInputValue] = useState("");

  return (
    <Command
      items={commandGroups}
      open={open}
      onOpenChange={setOpen}
      inputValue={inputValue}
      onInputValueChange={setInputValue}
      trigger={<Button type="button">Open command menu</Button>}
    />
  );
}

Groups and icons

Groups separate related items with labeled dividers. Use icon for a compact visual cue.

import { useState } from "react";
import { Button, Command } from "density-base-ui";
import { FileText, Settings, Users } from "lucide-react";

const commandGroups = [
  {
    id: "workspace",
    label: "Workspace",
    items: [
      {
        id: "projects",
        label: "Projects",
        icon: <FileText size={16} />,
        onSelect: () => {},
      },
      {
        id: "team",
        label: "Team",
        icon: <Users size={16} />,
        onSelect: () => {},
      },
    ],
  },
  {
    id: "settings",
    label: "Preferences",
    items: [
      {
        id: "settings",
        label: "Settings",
        icon: <Settings size={16} />,
        onSelect: () => {},
      },
    ],
  },
];

export default function CommandGroupsExample() {
  const [open, setOpen] = useState(false);
  const [inputValue, setInputValue] = useState("");

  return (
    <Command
      items={commandGroups}
      open={open}
      onOpenChange={setOpen}
      inputValue={inputValue}
      onInputValueChange={setInputValue}
      trigger={<Button type="button">Open grouped commands</Button>}
    />
  );
}

Shortcuts

import { useState } from "react";
import { Button, Command } from "density-base-ui";

const commandGroups = [
  {
    id: "actions",
    label: "Actions",
    items: [
      {
        id: "new-report",
        label: "New report",
        shortcut: "⌘ N",
        onSelect: () => {},
      },
      {
        id: "open-settings",
        label: "Open settings",
        shortcut: "⌘ ,",
        onSelect: () => {},
      },
    ],
  },
];

export default function CommandShortcutsExample() {
  const [open, setOpen] = useState(false);
  const [inputValue, setInputValue] = useState("");

  return (
    <Command
      items={commandGroups}
      open={open}
      onOpenChange={setOpen}
      inputValue={inputValue}
      onInputValueChange={setInputValue}
      trigger={<Button type="button">Open action commands</Button>}
    />
  );
}

Filtering and empty state

Filtering matches item labels and keywords. Try people or a query with no matches.

import { useState } from "react";
import { Button, Command } from "density-base-ui";

const commandGroups = [
  {
    id: "workspace",
    items: [
      { id: "projects", label: "Projects", onSelect: () => {} },
      { id: "team", label: "Team", keywords: ["people"], onSelect: () => {} },
    ],
  },
];

export default function CommandFilteringExample() {
  const [open, setOpen] = useState(false);
  const [inputValue, setInputValue] = useState("");

  return (
    <Command
      items={commandGroups}
      open={open}
      onOpenChange={setOpen}
      inputValue={inputValue}
      onInputValueChange={setInputValue}
      emptyLabel="No matching workspace action"
      trigger={<Button type="button">Open workspace commands</Button>}
    />
  );
}

Global shortcut

Omit trigger when the consuming view owns how the palette opens, such as a global Cmd/Ctrl-K shortcut.

import { useEffect, useState } from "react";
import { Button, Command } from "density-base-ui";

const commandGroups = [
  {
    id: "actions",
    items: [{ id: "search", label: "Search", onSelect: () => {} }],
  },
];

export default function CommandGlobalShortcutExample() {
  const [open, setOpen] = useState(false);
  const [inputValue, setInputValue] = useState("");

  useEffect(() => {
    const handleKeyDown = (event: KeyboardEvent) => {
      if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === "k") {
        event.preventDefault();
        setOpen(true);
      }
    };

    window.addEventListener("keydown", handleKeyDown);
    return () => window.removeEventListener("keydown", handleKeyDown);
  }, []);

  return (
    <>
      <Button type="button" onClick={() => setOpen(true)}>
        Open with ⌘ K
      </Button>
      <Command
        items={commandGroups}
        open={open}
        onOpenChange={setOpen}
        inputValue={inputValue}
        onInputValueChange={setInputValue}
      />
    </>
  );
}

Each CommandGroup has an id, optional label, and items. An item has id, label, required onSelect, and optional keywords, icon, shortcut, disabled, and color props.

Title

import { useState } from "react";
import { Button, Command } from "density-base-ui";

const commandGroups = [
  {
    id: "pages",
    items: [{ id: "reports", label: "Reports", onSelect: () => {} }],
  },
];

export default function CommandTitleExample() {
  const [open, setOpen] = useState(false);
  const [inputValue, setInputValue] = useState("");

  return (
    <Command
      items={commandGroups}
      open={open}
      onOpenChange={setOpen}
      inputValue={inputValue}
      onInputValueChange={setInputValue}
      trigger={<Button type="button">Open command menu</Button>}
      title="Go to"
    />
  );
}

Placeholder

import { useState } from "react";
import { Button, Command } from "density-base-ui";

const commandGroups = [
  {
    id: "pages",
    items: [{ id: "reports", label: "Reports", onSelect: () => {} }],
  },
];

export default function CommandPlaceholderExample() {
  const [open, setOpen] = useState(false);
  const [inputValue, setInputValue] = useState("");

  return (
    <Command
      items={commandGroups}
      open={open}
      onOpenChange={setOpen}
      inputValue={inputValue}
      onInputValueChange={setInputValue}
      trigger={<Button type="button">Open command menu</Button>}
      placeholder="Search reports"
    />
  );
}

Empty label

import { useState } from "react";
import { Button, Command } from "density-base-ui";

const commandGroups = [
  {
    id: "pages",
    items: [{ id: "reports", label: "Reports", onSelect: () => {} }],
  },
];

export default function CommandEmptyLabelExample() {
  const [open, setOpen] = useState(false);
  const [inputValue, setInputValue] = useState("");

  return (
    <Command
      items={commandGroups}
      open={open}
      onOpenChange={setOpen}
      inputValue={inputValue}
      onInputValueChange={setInputValue}
      trigger={<Button type="button">Open command menu</Button>}
      emptyLabel="No matching reports"
    />
  );
}

Size

import { useState } from "react";
import { Button, Command, View } from "density-base-ui";

const commandGroups = [
  {
    id: "pages",
    items: [{ id: "reports", label: "Reports", onSelect: () => {} }],
  },
];

export default function CommandSizeExample() {
  const [open, setOpen] = useState(false);
  const [inputValue, setInputValue] = useState("");

  return (
    <View align="center" gap="md" orientation="horizontal" padding={false} wrap>
      <Command
        items={commandGroups}
        open={open}
        onOpenChange={setOpen}
        inputValue={inputValue}
        onInputValueChange={setInputValue}
        trigger={<Button type="button">2xs</Button>}
        size="2xs"
      />
      <Command
        items={commandGroups}
        open={open}
        onOpenChange={setOpen}
        inputValue={inputValue}
        onInputValueChange={setInputValue}
        trigger={<Button type="button">xs</Button>}
        size="xs"
      />
      <Command
        items={commandGroups}
        open={open}
        onOpenChange={setOpen}
        inputValue={inputValue}
        onInputValueChange={setInputValue}
        trigger={<Button type="button">sm</Button>}
        size="sm"
      />
      <Command
        items={commandGroups}
        open={open}
        onOpenChange={setOpen}
        inputValue={inputValue}
        onInputValueChange={setInputValue}
        trigger={<Button type="button">md</Button>}
        size="md"
      />
      <Command
        items={commandGroups}
        open={open}
        onOpenChange={setOpen}
        inputValue={inputValue}
        onInputValueChange={setInputValue}
        trigger={<Button type="button">lg</Button>}
        size="lg"
      />
      <Command
        items={commandGroups}
        open={open}
        onOpenChange={setOpen}
        inputValue={inputValue}
        onInputValueChange={setInputValue}
        trigger={<Button type="button">xl</Button>}
        size="xl"
      />
      <Command
        items={commandGroups}
        open={open}
        onOpenChange={setOpen}
        inputValue={inputValue}
        onInputValueChange={setInputValue}
        trigger={<Button type="button">2xl</Button>}
        size="2xl"
      />
      <Command
        items={commandGroups}
        open={open}
        onOpenChange={setOpen}
        inputValue={inputValue}
        onInputValueChange={setInputValue}
        trigger={<Button type="button">3xl</Button>}
        size="3xl"
      />
    </View>
  );
}

Depth

import { useState } from "react";
import { Button, Command, View } from "density-base-ui";

const commandGroups = [
  {
    id: "pages",
    items: [{ id: "reports", label: "Reports", onSelect: () => {} }],
  },
];

export default function CommandDepthExample() {
  const [open, setOpen] = useState(false);
  const [inputValue, setInputValue] = useState("");

  return (
    <View align="center" gap="md" orientation="horizontal" padding={false} wrap>
      <Command
        items={commandGroups}
        open={open}
        onOpenChange={setOpen}
        inputValue={inputValue}
        onInputValueChange={setInputValue}
        trigger={<Button type="button">1</Button>}
        depth="1"
      />
      <Command
        items={commandGroups}
        open={open}
        onOpenChange={setOpen}
        inputValue={inputValue}
        onInputValueChange={setInputValue}
        trigger={<Button type="button">2</Button>}
        depth="2"
      />
      <Command
        items={commandGroups}
        open={open}
        onOpenChange={setOpen}
        inputValue={inputValue}
        onInputValueChange={setInputValue}
        trigger={<Button type="button">3</Button>}
        depth="3"
      />
      <Command
        items={commandGroups}
        open={open}
        onOpenChange={setOpen}
        inputValue={inputValue}
        onInputValueChange={setInputValue}
        trigger={<Button type="button">4</Button>}
        depth="4"
      />
      <Command
        items={commandGroups}
        open={open}
        onOpenChange={setOpen}
        inputValue={inputValue}
        onInputValueChange={setInputValue}
        trigger={<Button type="button">5</Button>}
        depth="5"
      />
      <Command
        items={commandGroups}
        open={open}
        onOpenChange={setOpen}
        inputValue={inputValue}
        onInputValueChange={setInputValue}
        trigger={<Button type="button">6</Button>}
        depth="6"
      />
      <Command
        items={commandGroups}
        open={open}
        onOpenChange={setOpen}
        inputValue={inputValue}
        onInputValueChange={setInputValue}
        trigger={<Button type="button">7</Button>}
        depth="7"
      />
    </View>
  );
}

Padding

import { useState } from "react";
import { Button, Command, View } from "density-base-ui";

const commandGroups = [
  {
    id: "pages",
    items: [{ id: "reports", label: "Reports", onSelect: () => {} }],
  },
];

type CommandPaddingPreviewProps = {
  padding: boolean;
  triggerLabel: string;
};

function CommandPaddingPreview({
  padding,
  triggerLabel,
}: CommandPaddingPreviewProps) {
  const [open, setOpen] = useState(false);
  const [inputValue, setInputValue] = useState("");

  return (
    <Command
      items={commandGroups}
      open={open}
      onOpenChange={setOpen}
      inputValue={inputValue}
      onInputValueChange={setInputValue}
      trigger={<Button type="button">{triggerLabel}</Button>}
      padding={padding}
    />
  );
}

export default function CommandPaddingExample() {
  return (
    <View align="center" gap="md" orientation="horizontal" padding={false} wrap>
      <CommandPaddingPreview padding={false} triggerLabel="None" />
      <CommandPaddingPreview padding triggerLabel="Padding enabled" />
    </View>
  );
}