Command Palette
@sushindustries/uiSearch over everything the host can name, in a native dialog: substring filter, arrow keys, and the host keeps the router.
Examples are the tab where the component is shown doing a job, not demonstrating a prop. The API tab already lists the props.
tanstacktanstack add https://adamjurek.com/r/tanstack/command-palette.jsonpnpm dlx shadcn@latest add https://adamjurek.com/r/shadcn/command-palette.jsonPress Compare. The frames are real viewports, so a layout that breaks at 320 breaks here too rather than in somebody's hands.
In a page
import { useEffect, useState } from "react";
import { useNavigate } from "@tanstack/react-router";
import { CommandPalette } from "@sushindustries/ui";
import { searchEntries } from "./search.catalogue";
export function SiteChrome({ children }: { children: React.ReactNode }) {
const [open, setOpen] = useState(false);
const navigate = useNavigate();
useEffect(() => {
function onKeyDown(event: KeyboardEvent) {
if (event.key === "k" && (event.metaKey || event.ctrlKey)) {
event.preventDefault();
setOpen(true);
}
}
window.addEventListener("keydown", onKeyDown);
return () => window.removeEventListener("keydown", onKeyDown);
}, []);
return (
<>
{children}
<CommandPalette
entries={searchEntries}
open={open}
onClose={() => setOpen(false)}
onSelect={(entry) => {
setOpen(false);
navigate({ to: entry.href });
}}
/>
</>
);
}tsx
What this example is not
The ⌘K listener here is the host's own effect, not something
CommandPalette sets up for you - the component only reacts to open.
navigate from the router replaces the plain window.location write
from Get Started, which is the difference between a full page load and a
client-side transition.