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.
| Prop | Type | Default | Description |
|---|---|---|---|
open | boolean | Whether the dialog is open. | |
onOpenChange | (open: boolean) => void | Callback when dialog state changes. | |
backdropBlur | boolean | false | Enable backdrop blur for focused view. |
blurIntensity | number | 8 | Blur intensity in pixels (when backdropBlur on). |
children | React.ReactNode | Content components. |
CommandBoxTrigger
A styled trigger button showing ⌘K / Ctrl K.
| Prop | Type | Default | Description |
|---|---|---|---|
onClick | () => void | Callback when button is clicked. | |
className | string | Additional CSS classes. |
CommandBoxFooter
Footer with customizable logo and action hints.
| Prop | Type | Default | Description |
|---|---|---|---|
logo | React.ReactNode | Custom logo component (SVG, icon, etc.) | |
logoSrc | string | Image URL for logo (PNG, JPG, SVG, WebP, etc.) | |
logoAlt | string | "Logo" | Alt text for logo image. |
logoSize | number | 24 | Size of the logo image in pixels. |
commandMetaMap | Map<string, { commandKind: CommandKind }> | Map titles to command kinds for action labels. | |
actionLabels | Record<CommandKind, string> | See below | Custom 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),
});| Option | Type | Default | Description |
|---|---|---|---|
defaultOpen | boolean | false | Initial open state. |
hotkeys | string | "mod+k, slash" | Keyboard shortcut(s) to toggle. |
onKeyboardOpen | (key: string) => void | Callback when opened via keyboard. |
Other Components
| Component | Description |
|---|---|
CommandBoxInput | Search input with placeholder. |
CommandBoxList | Scrollable list container. |
CommandBoxEmpty | Empty state message. |
CommandBoxGroup | Group with heading. |
CommandBoxItem | Individual command item. |
CommandBoxLinkGroup | Convenience component for link groups. |
CommandBoxSeparator | Horizontal separator. |
Customizing the Footer Logo
The CommandBoxFooter accepts either a React component or an image URL:
Using a React Component (recommended for SVG icons)
<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>
</>
);
}