Six months into a real SaaS, the navigation has grown three sidebars deep and your power users are tired of clicking. Linear, Slack, GitHub, and Notion have trained everyone to expect Cmd+K. The first time a customer says "wait, you don't have a command palette?", you know it's no longer optional.
This feature installs a keyboard-first launcher built with LiveView. It is not a generic search bar — it is a typed, protocol-driven result list that you teach about your domain by implementing one small protocol per resource type.
Make every page and record one hotkey away
A command palette is more than a UX nicety in a multi-tenant SaaS. It is the difference between an operator who can support a customer in twenty seconds and one who has to remember which submenu hides the team settings. The implementation pattern here keeps the search logic in your CommandPalette context and the formatting in a protocol you implement per type — so the palette grows with the product instead of becoming another tangle of cond statements.
What This Feature Adds
- A
CommandPaletteLiveLiveView with a search input, result list, and keyboard navigation - A
MyAppWeb.CommandPalette.Formatprotocol withheader/1,label/1, andlink/2so each searchable type formats its own results - A
Resultstruct so results are typed consistently regardless of source - A
CommandPalettecontext that searches across configurable contexts (app, andadminwhen the admin feature is installed) - A
hotkeys.jsimport added toassets/js/app.jssoCmd+K/Ctrl+Kopens the palette anywhere in the app
How It Fits Into Your Phoenix Application
The palette is a LiveView you live_render into your layout. The protocol-based formatting is the part worth paying attention to: you teach the palette about a new resource by implementing MyAppWeb.CommandPalette.Format for the struct, not by editing the LiveView itself.
defimpl MyAppWeb.CommandPalette.Format, for: MyApp.Accounts.User do
def header(_), do: "Users"
def label(user), do: "#{user.name} (#{user.email})"
def link(user, :admin), do: ~p"/admin/users/#{user.id}"
def link(user, :app), do: ~p"/users/#{user.id}"
end
The context argument lets the palette show different links to admins than to regular users for the same record.
Installation Notes
- No required dependencies. The palette works standalone, but it composes naturally with
admin(which adds anadmincontext) andlayouts(which gives it a sensible mount point). - Drop the palette into your layout once. After that, adding new searchable types is a
defimpl, not another render call. - Hotkey conflicts are rare with
Cmd+K, butCtrl+Koverlaps with the browser address bar shortcut on some platforms — test the binding in your target browsers.
Build This With Live SaaS Kit
Install saas_kit, then mix saaskit.feature.install command_palette to wire up Cmd+K navigation across your app and start teaching it your domain one protocol at a time.