Components Command

Petal Pro is the full SaaS app this is built for

Auth, billing, admin, and Claude Code integration included. One purchase, unlimited projects.

Command

The ⌘K menu. Type to filter, arrow keys to move, Enter to run. Filtering happens in the PetalCommand hook so keystrokes never wait on the server, and every item is a real link or button - so navigate, patch and any phx-* binding work exactly as they do everywhere else in LiveView. The markup follows the WAI-ARIA combobox pattern, so it's keyboard and screen-reader friendly out of the box.
Inline palette

A palette panel you drop into a page, sidebar or picker. Type to filter; arrow keys move, Enter runs.

heex
<div class="w-full max-w-md mx-auto">
<.command
id="showcase-command"
loop
class="border border-gray-200 shadow-xs dark:border-white/15"
>
<.command_input placeholder="Type a command or search..." />
<.command_list>
<.command_empty>No results found.</.command_empty>
<.command_group heading="Suggestions">
<.command_item keywords={["date", "schedule"]}>
<.icon name="hero-calendar" /> Calendar
</.command_item>
<.command_item keywords={["smile"]}>
<.icon name="hero-face-smile" /> Search emoji
</.command_item>
<.command_item disabled>
<.icon name="hero-calculator" /> Calculator
</.command_item>
</.command_group>
<.command_separator />
<.command_group heading="Settings">
<.command_item keywords={["account", "user"]}>
<.icon name="hero-user" /> Profile
<.command_shortcut>⌘P</.command_shortcut>
</.command_item>
<.command_item keywords={["payment", "card"]}>
<.icon name="hero-credit-card" /> Billing
<.command_shortcut>⌘B</.command_shortcut>
</.command_item>
</.command_group>
</.command_list>
</.command>
</div>
The ⌘K dialog

The same palette in a native <dialog> - top layer, focus trap and Escape for free. Open it from any element.

heex
<div class="flex flex-col items-center gap-3">
<.button
color="gray"
variant="outline"
phx-click={PetalComponents.Command.open_command("showcase-cmdk")}
>
<.icon name="hero-magnifying-glass" class="w-4 h-4 mr-1" /> Open command palette
</.button>
<.command_dialog id="showcase-cmdk" shortcut="">
<.command_input placeholder="Search components and actions..." />
<.command_list>
<.command_empty>Nothing matches. Try a component name.</.command_empty>
<.command_group heading="Navigation">
<.command_item keywords={["home"]}>
<.icon name="hero-home" /> Overview
</.command_item>
<.command_item keywords={["ui", "components"]}>
<.icon name="hero-squares-2x2" /> Components
</.command_item>
<.command_item keywords={["price", "plans"]}>
<.icon name="hero-credit-card" /> Pricing
</.command_item>
</.command_group>
</.command_list>
</.command_dialog>
</div>
Filtering and keywords

The hook matches the visible label plus any keywords you add, so "schedule" can find Calendar even though the word never appears on screen. Items are hidden, never reordered - the server owns DOM order, which keeps everything safe under LiveView patches. Pass loop to wrap the arrow keys from the last item back to the first.

Properties

The two shells - command/1 and command_dialog/1 - plus the command_item/1 each row is built from. The pieces in between (command_input/1, command_list/1, command_empty/1, command_group/1, command_separator/1, command_shortcut/1) take the obvious props.

<.command>
Attribute Type Default Description
class any nil extra classes for the palette panel
id* string unique id; the PetalCommand hook mounts here
loop boolean false arrow keys wrap from the last item to the first and back
rest global
:inner_block slot slot
<.command_dialog>
Attribute Type Default Description
class any nil extra classes for the palette panel inside the dialog
id* string unique id for the dialog; open it with ⌘K or open_command/1
loop boolean false arrow keys wrap around the list
reset_on_close boolean true clear the query (and restore all items) when the dialog closes
rest global
shortcut string "k" the key bound with Cmd (mac) / Ctrl to toggle the dialog. Set to nil to disable the global binding
:inner_block slot slot
<.command_item>
Attribute Type Default Description
class any nil
disabled boolean false
href string nil plain link target - renders the item as a link
keywords list [] extra search aliases for this item
navigate string nil live_redirect target - renders the item as a link
patch string nil live_patch target - renders the item as a link
rest global phx-click and friends work here - Enter clicks the highlighted item
value string nil the text the filter matches against. Defaults to the item's visible text
:inner_block slot slot