Skip to content
Template Empire / components

Search components

10 results

Browse components

Command Palette

A free React command palette modal built on the native dialog element, with a spring entrance and full combobox keyboard navigation.

  • Automated axe check
  • Reduced-motion tested
  • SSR build verified
  • Motion
  • 2 runtime packages

Command Palette is a free command palette component for Next.js and React, built with Motion and Tailwind on top of the native dialog element. Opening it fades in a blurred backdrop while the panel springs into place near the top of the viewport, and typing filters the command list with a case-insensitive substring match: surviving rows fade in with a 25 millisecond stagger capped at the first eight rows, while filtered-out rows are removed instantly so typing always feels crisp.

The input follows the WAI-ARIA combobox pattern, with aria-expanded, aria-controls and aria-activedescendant wired to a listbox of option rows. Arrow keys move the active option and its highlight pill, which springs between rows using a Motion layoutId, Enter runs the active command and closes, Escape closes, and keyboard focus never leaves the input while the palette is open. The component is fully controlled through open and onClose, so the usual Cmd K or Ctrl K binding lives in your page code, and with prefers-reduced-motion enabled every animation becomes a plain fade of 150 milliseconds or less.

Every action, one keystroke away

Press ⌘K (or Ctrl K), or use the button, then type to filter and hit Enter to run a command.

No command run yet

Install

Add the @te registry to your components.json once:

{
  "registries": {
    "@te": "https://templateempire.io/r/{name}.json"
  }
}

Then install the component:

npx shadcn@latest add @te/modal-command-palette

Usage

"use client";

import { useState } from "react";
import { ModalCommandPalette } from "@/components/modal-command-palette";

export default function Page() {
  const [open, setOpen] = useState(false);

  return (
    <>
      <button type="button" onClick={() => setOpen(true)}>
        Open command palette
      </button>
      <ModalCommandPalette
        open={open}
        onClose={() => setOpen(false)}
        commands={[
          {
            id: "new-doc",
            label: "New document",
            hint: "⌘N",
            onSelect: () => createDocument(),
          },
          {
            id: "settings",
            label: "Open settings",
            onSelect: () => openSettings(),
          },
        ]}
      />
    </>
  );
}

Props

PropTypeDefaultDescription
openbooleanrequiredControlled open state.
onClose() => voidrequiredCalled when the palette asks to close: Escape, a click on the backdrop or a command selection.
commands{ id: string; label: string; hint?: string; onSelect: () => void }[]requiredFlat command list. label is matched by the substring filter, hint renders right-aligned, and onSelect runs when the command is chosen.
placeholderstring"Type a command…"Input placeholder.
classNamestringundefinedAdditional classes merged onto the panel.

Customisation

Command Palette declares no tokens of its own; the panel, rows and highlight consume the global semantic tokens through Tailwind utilities, and both the panel entrance and the highlight pill use the shared springSnappy preset from lib/motion.ts, so retuning that preset retunes every springing component together. The className prop is merged onto the panel, which is the usual place to change its width or corner radius:

:root {
  --border: oklch(from var(--color-neutral-50) l c h / 0.16);
}

<ModalCommandPalette
  open={open}
  onClose={() => setOpen(false)}
  commands={commands}
  placeholder="Search actions…"
  className="max-w-xl rounded-2xl"
/>

Frequently asked questions

Is Command Palette free for commercial use?
Yes. Command Palette is MIT licensed, so you can use it in personal and commercial projects without attribution.
Is Command Palette accessible for keyboard and screen reader users?
Yes. It renders a native dialog element opened with showModal, so the browser provides the focus trap, top layer rendering and Escape handling. The search input implements the combobox pattern with aria-expanded, aria-controls and aria-activedescendant pointing into a listbox of option rows, arrow keys move the active option while focus stays in the input, Enter runs the active command, an empty result set announces No results through a disabled option in the listbox, and when the palette closes focus returns immediately to the element that opened it.
How do I bind Cmd K or Ctrl K to open the palette?
The component is fully controlled and ships no global listeners, so you add a keydown listener in your own page or layout that toggles the open state when the user presses Cmd K or Ctrl K. This keeps the shortcut in your hands: you can scope it per page, change the key combination, or disable it entirely without touching the component.

Related components

Want the full page this belongs to?Template Empire builds complete Next.js templates with this motion system throughout. Browse the paid templates that pair with these free components.Explore Template Empire templates