Command Palette

Search for a command to run...

54
Blog
PreviousNext

Command Box

A customizable command palette/search box with keyboard shortcuts and footer logo customization.

Loading...
"use client";
 
import {
  BookmarkIcon,
  BriefcaseBusinessIcon,
  HeartIcon,
  LayersIcon,
  MoonStarIcon,
  QuoteIcon,
  RssIcon,
  ShieldCheckIcon,
  SunMediumIcon,
  TextInitialIcon,
} from "lucide-react";
import { useRouter } from "next/navigation";
import { useTheme } from "next-themes";
import { useCallback } from "react";
import { toast } from "sonner";
 
import { Icons } from "@/components/icons";
import { JyotirmoyMark } from "@/components/jyotirmoy-mark";
import { trackEvent } from "@/lib/events";
import {
  CommandBox,
  CommandBoxEmpty,
  CommandBoxFooter,
  CommandBoxGroup,
  CommandBoxInput,
  CommandBoxItem,
  CommandBoxLinkGroup,
  CommandBoxList,
  CommandBoxTrigger,
  type CommandKind,
  type CommandLinkItem,
  useCommandBox,
} from "@/components/jyotirmoydas/command-box";
 
// Demo menu links
const MENU_LINKS: CommandLinkItem[] = [
  { title: "Portfolio", href: "/", icon: JyotirmoyMark },
  { title: "Components", href: "/components", icon: Icons.react },
  { title: "Blog", href: "/blog", icon: RssIcon },
];
 
const PORTFOLIO_LINKS: CommandLinkItem[] = [
  { title: "About", href: "/#about", icon: TextInitialIcon },
  { title: "Testimonials", href: "/#testimonials", icon: QuoteIcon },
  { title: "Tech Stack", href: "/#stack", icon: LayersIcon },
  { title: "Experience", href: "/#experience", icon: BriefcaseBusinessIcon },
  { title: "Projects", href: "/#projects", icon: BookmarkIcon },
  { title: "Certificates", href: "/#certificates", icon: ShieldCheckIcon },
];
 
const OTHER_LINKS: CommandLinkItem[] = [
  { title: "Sponsors", href: "/sponsors", icon: HeartIcon },
  { title: "RSS Feed", href: "/rss", icon: RssIcon, openInNewTab: true },
];
 
// Command meta map for footer action labels
const COMMAND_META_MAP = new Map<string, { commandKind: CommandKind }>([
  ["Light", { commandKind: "command" }],
  ["Dark", { commandKind: "command" }],
  ["Auto", { commandKind: "command" }],
]);
 
export function CommandBoxDemo() {
  const router = useRouter();
  const { setTheme } = useTheme();
 
  // Use the hook for keyboard shortcuts
  const { open, setOpen } = useCommandBox({
    onKeyboardOpen: (key) => {
      trackEvent({
        name: "open_command_menu",
        properties: { method: "keyboard", key },
      });
    },
  });
 
  const handleOpenLink = useCallback(
    (href: string, openInNewTab = false) => {
      setOpen(false);
      if (openInNewTab) {
        window.open(href, "_blank", "noopener");
      } else {
        router.push(href);
      }
    },
    [router, setOpen]
  );
 
  const handleThemeChange = useCallback(
    (theme: "light" | "dark" | "system") => {
      setOpen(false);
      setTheme(theme);
      toast.success(`Theme changed to ${theme}`);
    },
    [setOpen, setTheme]
  );
 
  return (
    <>
      {/* Trigger button - matches site header */}
      <CommandBoxTrigger onClick={() => setOpen(true)} />
 
      {/* Command Box Dialog */}
      <CommandBox open={open} onOpenChange={setOpen}>
        <CommandBoxInput placeholder="Type a command or search..." />
 
        <CommandBoxList>
          <CommandBoxEmpty>No results found.</CommandBoxEmpty>
 
          <CommandBoxLinkGroup
            heading="Menu"
            links={MENU_LINKS}
            onLinkSelect={handleOpenLink}
          />
 
          <CommandBoxLinkGroup
            heading="Portfolio"
            links={PORTFOLIO_LINKS}
            onLinkSelect={handleOpenLink}
          />
 
          <CommandBoxGroup heading="Theme">
            <CommandBoxItem
              keywords={["theme"]}
              onSelect={() => handleThemeChange("light")}
            >
              <SunMediumIcon />
              Light
            </CommandBoxItem>
            <CommandBoxItem
              keywords={["theme"]}
              onSelect={() => handleThemeChange("dark")}
            >
              <MoonStarIcon />
              Dark
            </CommandBoxItem>
            <CommandBoxItem
              keywords={["theme"]}
              onSelect={() => handleThemeChange("system")}
            >
              <Icons.contrast />
              Auto
            </CommandBoxItem>
          </CommandBoxGroup>
 
          <CommandBoxLinkGroup
            heading="Other"
            links={OTHER_LINKS}
            onLinkSelect={handleOpenLink}
          />
        </CommandBoxList>
 
        {/* Footer with custom logo */}
        <CommandBoxFooter
          logo={<JyotirmoyMark className="size-6 text-muted-foreground" />}
          commandMetaMap={COMMAND_META_MAP}
        />
      </CommandBox>
    </>
  );
}

Installation

bunx --bun shadcn@latest add @jyotirmoydas/command-box

Usage

import {
  CommandBox,
  CommandBoxEmpty,
  CommandBoxFooter,
  CommandBoxGroup,
  CommandBoxInput,
  CommandBoxItem,
  CommandBoxList,
  CommandBoxTrigger,
  useCommandBox,
} from "@/components/jyotirmoydas/command-box";
const { open, setOpen } = useCommandBox();
 
<>
  <CommandBoxTrigger onClick={() => setOpen(true)} />
 
  <CommandBox open={open} onOpenChange={setOpen}>
    <CommandBoxInput placeholder="Search..." />
    <CommandBoxList>
      <CommandBoxEmpty>No results found.</CommandBoxEmpty>
      <CommandBoxGroup heading="Actions">
        <CommandBoxItem onSelect={() => console.log("selected")}>
          Action 1
        </CommandBoxItem>
      </CommandBoxGroup>
    </CommandBoxList>
    <CommandBoxFooter logo={<MyLogo />} />
  </CommandBox>
</>;

API Reference

CommandBox

The main dialog wrapper component.

PropTypeDefaultDescription
openbooleanWhether the dialog is open.
onOpenChange(open: boolean) => voidCallback when dialog state changes.
backdropBlurbooleanfalseEnable backdrop blur for focused view.
blurIntensitynumber8Blur intensity in pixels (when backdropBlur on).
childrenReact.ReactNodeContent components.

CommandBoxTrigger

A styled trigger button showing ⌘K / Ctrl K.

PropTypeDefaultDescription
onClick() => voidCallback when button is clicked.
classNamestringAdditional CSS classes.

CommandBoxFooter

Footer with customizable logo and action hints.

PropTypeDefaultDescription
logoReact.ReactNodeCustom logo component (SVG, icon, etc.)
logoSrcstringImage URL for logo (PNG, JPG, SVG, WebP, etc.)
logoAltstring"Logo"Alt text for logo image.
logoSizenumber24Size of the logo image in pixels.
commandMetaMapMap<string, { commandKind: CommandKind }>Map titles to command kinds for action labels.
actionLabelsRecord<CommandKind, string>See belowCustom labels for action hints.

Default action labels:

  • command: "Run Command"
  • page: "Go to Page"
  • link: "Open Link"

useCommandBox

Hook to manage command box state with keyboard shortcuts.

const { open, setOpen, toggle } = useCommandBox({
  defaultOpen: false,
  hotkeys: "mod+k, slash",
  onKeyboardOpen: (key) => console.log(key),
});
OptionTypeDefaultDescription
defaultOpenbooleanfalseInitial open state.
hotkeysstring"mod+k, slash"Keyboard shortcut(s) to toggle.
onKeyboardOpen(key: string) => voidCallback when opened via keyboard.

Other Components

ComponentDescription
CommandBoxInputSearch input with placeholder.
CommandBoxListScrollable list container.
CommandBoxEmptyEmpty state message.
CommandBoxGroupGroup with heading.
CommandBoxItemIndividual command item.
CommandBoxLinkGroupConvenience component for link groups.
CommandBoxSeparatorHorizontal separator.

The CommandBoxFooter accepts either a React component or an image URL:

<CommandBoxFooter
  logo={<MyCustomIcon className="size-6 text-muted-foreground" />}
/>

Using an Image URL (PNG, JPG, SVG, WebP)

<CommandBoxFooter
  logoSrc="/path/to/logo.png"
  logoAlt="My App Logo"
  logoSize={24}
/>

Examples

Default

A command box with all standard features.

Loading...
"use client";
 
import {
  BookmarkIcon,
  BriefcaseBusinessIcon,
  HeartIcon,
  LayersIcon,
  MoonStarIcon,
  QuoteIcon,
  RssIcon,
  ShieldCheckIcon,
  SunMediumIcon,
  TextInitialIcon,
} from "lucide-react";
import { useRouter } from "next/navigation";
import { useTheme } from "next-themes";
import { useCallback } from "react";
import { toast } from "sonner";
 
import { Icons } from "@/components/icons";
import { JyotirmoyMark } from "@/components/jyotirmoy-mark";
import { trackEvent } from "@/lib/events";
import {
  CommandBox,
  CommandBoxEmpty,
  CommandBoxFooter,
  CommandBoxGroup,
  CommandBoxInput,
  CommandBoxItem,
  CommandBoxLinkGroup,
  CommandBoxList,
  CommandBoxTrigger,
  type CommandKind,
  type CommandLinkItem,
  useCommandBox,
} from "@/components/jyotirmoydas/command-box";
 
// Demo menu links
const MENU_LINKS: CommandLinkItem[] = [
  { title: "Portfolio", href: "/", icon: JyotirmoyMark },
  { title: "Components", href: "/components", icon: Icons.react },
  { title: "Blog", href: "/blog", icon: RssIcon },
];
 
const PORTFOLIO_LINKS: CommandLinkItem[] = [
  { title: "About", href: "/#about", icon: TextInitialIcon },
  { title: "Testimonials", href: "/#testimonials", icon: QuoteIcon },
  { title: "Tech Stack", href: "/#stack", icon: LayersIcon },
  { title: "Experience", href: "/#experience", icon: BriefcaseBusinessIcon },
  { title: "Projects", href: "/#projects", icon: BookmarkIcon },
  { title: "Certificates", href: "/#certificates", icon: ShieldCheckIcon },
];
 
const OTHER_LINKS: CommandLinkItem[] = [
  { title: "Sponsors", href: "/sponsors", icon: HeartIcon },
  { title: "RSS Feed", href: "/rss", icon: RssIcon, openInNewTab: true },
];
 
// Command meta map for footer action labels
const COMMAND_META_MAP = new Map<string, { commandKind: CommandKind }>([
  ["Light", { commandKind: "command" }],
  ["Dark", { commandKind: "command" }],
  ["Auto", { commandKind: "command" }],
]);
 
export function CommandBoxDemo() {
  const router = useRouter();
  const { setTheme } = useTheme();
 
  // Use the hook for keyboard shortcuts
  const { open, setOpen } = useCommandBox({
    onKeyboardOpen: (key) => {
      trackEvent({
        name: "open_command_menu",
        properties: { method: "keyboard", key },
      });
    },
  });
 
  const handleOpenLink = useCallback(
    (href: string, openInNewTab = false) => {
      setOpen(false);
      if (openInNewTab) {
        window.open(href, "_blank", "noopener");
      } else {
        router.push(href);
      }
    },
    [router, setOpen]
  );
 
  const handleThemeChange = useCallback(
    (theme: "light" | "dark" | "system") => {
      setOpen(false);
      setTheme(theme);
      toast.success(`Theme changed to ${theme}`);
    },
    [setOpen, setTheme]
  );
 
  return (
    <>
      {/* Trigger button - matches site header */}
      <CommandBoxTrigger onClick={() => setOpen(true)} />
 
      {/* Command Box Dialog */}
      <CommandBox open={open} onOpenChange={setOpen}>
        <CommandBoxInput placeholder="Type a command or search..." />
 
        <CommandBoxList>
          <CommandBoxEmpty>No results found.</CommandBoxEmpty>
 
          <CommandBoxLinkGroup
            heading="Menu"
            links={MENU_LINKS}
            onLinkSelect={handleOpenLink}
          />
 
          <CommandBoxLinkGroup
            heading="Portfolio"
            links={PORTFOLIO_LINKS}
            onLinkSelect={handleOpenLink}
          />
 
          <CommandBoxGroup heading="Theme">
            <CommandBoxItem
              keywords={["theme"]}
              onSelect={() => handleThemeChange("light")}
            >
              <SunMediumIcon />
              Light
            </CommandBoxItem>
            <CommandBoxItem
              keywords={["theme"]}
              onSelect={() => handleThemeChange("dark")}
            >
              <MoonStarIcon />
              Dark
            </CommandBoxItem>
            <CommandBoxItem
              keywords={["theme"]}
              onSelect={() => handleThemeChange("system")}
            >
              <Icons.contrast />
              Auto
            </CommandBoxItem>
          </CommandBoxGroup>
 
          <CommandBoxLinkGroup
            heading="Other"
            links={OTHER_LINKS}
            onLinkSelect={handleOpenLink}
          />
        </CommandBoxList>
 
        {/* Footer with custom logo */}
        <CommandBoxFooter
          logo={<JyotirmoyMark className="size-6 text-muted-foreground" />}
          commandMetaMap={COMMAND_META_MAP}
        />
      </CommandBox>
    </>
  );
}

Backdrop Blur

Enable backdropBlur to blur surrounding content for a focused view:

<CommandBox open={open} onOpenChange={setOpen} backdropBlur blurIntensity={10}>
  {/* ... */}
</CommandBox>
Loading...
"use client";
 
import {
  BookmarkIcon,
  BriefcaseBusinessIcon,
  HeartIcon,
  LayersIcon,
  MoonStarIcon,
  QuoteIcon,
  RssIcon,
  ShieldCheckIcon,
  SunMediumIcon,
  TextInitialIcon,
} from "lucide-react";
import { useRouter } from "next/navigation";
import { useTheme } from "next-themes";
import { useCallback } from "react";
import { toast } from "sonner";
 
import { Icons } from "@/components/icons";
import { JyotirmoyMark } from "@/components/jyotirmoy-mark";
import { trackEvent } from "@/lib/events";
import {
  CommandBox,
  CommandBoxEmpty,
  CommandBoxFooter,
  CommandBoxGroup,
  CommandBoxInput,
  CommandBoxItem,
  CommandBoxLinkGroup,
  CommandBoxList,
  CommandBoxTrigger,
  type CommandKind,
  type CommandLinkItem,
  useCommandBox,
} from "@/components/jyotirmoydas/command-box";
 
// Demo menu links
const MENU_LINKS: CommandLinkItem[] = [
  { title: "Portfolio", href: "/", icon: JyotirmoyMark },
  { title: "Components", href: "/components", icon: Icons.react },
  { title: "Blog", href: "/blog", icon: RssIcon },
];
 
const PORTFOLIO_LINKS: CommandLinkItem[] = [
  { title: "About", href: "/#about", icon: TextInitialIcon },
  { title: "Testimonials", href: "/#testimonials", icon: QuoteIcon },
  { title: "Tech Stack", href: "/#stack", icon: LayersIcon },
  { title: "Experience", href: "/#experience", icon: BriefcaseBusinessIcon },
  { title: "Projects", href: "/#projects", icon: BookmarkIcon },
  { title: "Certificates", href: "/#certificates", icon: ShieldCheckIcon },
];
 
const OTHER_LINKS: CommandLinkItem[] = [
  { title: "Sponsors", href: "/sponsors", icon: HeartIcon },
  { title: "RSS Feed", href: "/rss", icon: RssIcon, openInNewTab: true },
];
 
// Command meta map for footer action labels
const COMMAND_META_MAP = new Map<string, { commandKind: CommandKind }>([
  ["Light", { commandKind: "command" }],
  ["Dark", { commandKind: "command" }],
  ["Auto", { commandKind: "command" }],
]);
 
export function CommandBoxBlurDemo() {
  const router = useRouter();
  const { setTheme } = useTheme();
 
  // Use the hook for keyboard shortcuts
  const { open, setOpen } = useCommandBox({
    onKeyboardOpen: (key) => {
      trackEvent({
        name: "open_command_menu",
        properties: { method: "keyboard", key },
      });
    },
  });
 
  const handleOpenLink = useCallback(
    (href: string, openInNewTab = false) => {
      setOpen(false);
      if (openInNewTab) {
        window.open(href, "_blank", "noopener");
      } else {
        router.push(href);
      }
    },
    [router, setOpen]
  );
 
  const handleThemeChange = useCallback(
    (theme: "light" | "dark" | "system") => {
      setOpen(false);
      setTheme(theme);
      toast.success(`Theme changed to ${theme}`);
    },
    [setOpen, setTheme]
  );
 
  return (
    <>
      {/* Trigger button - matches site header */}
      <CommandBoxTrigger onClick={() => setOpen(true)} />
 
      {/* Command Box Dialog with Backdrop Blur */}
      <CommandBox
        open={open}
        onOpenChange={setOpen}
        backdropBlur // Enable backdrop blur for focused view
        blurIntensity={10} // Blur intensity in pixels
      >
        <CommandBoxInput placeholder="Type a command or search..." />
 
        <CommandBoxList>
          <CommandBoxEmpty>No results found.</CommandBoxEmpty>
 
          <CommandBoxLinkGroup
            heading="Menu"
            links={MENU_LINKS}
            onLinkSelect={handleOpenLink}
          />
 
          <CommandBoxLinkGroup
            heading="Portfolio"
            links={PORTFOLIO_LINKS}
            onLinkSelect={handleOpenLink}
          />
 
          <CommandBoxGroup heading="Theme">
            <CommandBoxItem
              keywords={["theme"]}
              onSelect={() => handleThemeChange("light")}
            >
              <SunMediumIcon />
              Light
            </CommandBoxItem>
            <CommandBoxItem
              keywords={["theme"]}
              onSelect={() => handleThemeChange("dark")}
            >
              <MoonStarIcon />
              Dark
            </CommandBoxItem>
            <CommandBoxItem
              keywords={["theme"]}
              onSelect={() => handleThemeChange("system")}
            >
              <Icons.contrast />
              Auto
            </CommandBoxItem>
          </CommandBoxGroup>
 
          <CommandBoxLinkGroup
            heading="Other"
            links={OTHER_LINKS}
            onLinkSelect={handleOpenLink}
          />
        </CommandBoxList>
 
        {/* Footer with custom logo */}
        <CommandBoxFooter
          logo={<JyotirmoyMark className="size-6 text-muted-foreground" />}
          commandMetaMap={COMMAND_META_MAP}
        />
      </CommandBox>
    </>
  );
}