Skip to content

Command palette · Open source

Laravel Ninja Keys

A Cmd+K / Ctrl+K command palette for Laravel, with the actions defined in PHP.

Laravel 12 and 13 PHP 8.3+ v1.0.0 Free · MIT
Laravel Ninja Keys: A Cmd+K / Ctrl+K command palette for Laravel, with the actions defined in PHP.

What it solves

Adding the ninja-keys command palette to a Laravel app by hand means writing a script per layout, building the action list in JavaScript, and tracking which actions each user is allowed to see.

You register actions in PHP, hide the ones a user may not use with your Gate abilities, and render the palette with <x-ninja-keys />. ninja-keys 1.2.2 is bundled and self-hosted, so there is no JavaScript to write and no npm build step.

  • Actions built in PHP, with route() and href() for navigation
  • ->can() runs Gate abilities on the server, so hidden actions never reach the browser
  • define() callbacks run at render time with the current user and request
  • Submenus from parent and children ids, or from nested arrays
  • Works across Livewire wire:navigate without duplicate shortcuts
  • ninja-keys 1.2.2 bundled and self-hosted, with fixes for three upstream bugs
A Laravel dashboard with the command palette open over it: a search box reading Type a command or search, and actions grouped under Preferences, Billing and Navigate, each with its Alt keyboard shortcut.
A Laravel dashboard with the command palette open over it: a search box reading Type a command or search, and actions grouped under Preferences, Billing and Navigate, each with its Alt keyboard shortcut.

Install

Requires PHP 8.3+ and Laravel 12 and 13.

composer require amjadiqbal/laravel-ninja-keys

Usage

// app/Providers/AppServiceProvider.php
use AmjadIqbal\NinjaKeys\Facades\NinjaKeys;
use AmjadIqbal\NinjaKeys\Manager;

NinjaKeys::define(function (Manager $palette) {
    $palette->addAction('dashboard')->title('Dashboard')->mdIcon('dashboard')->route('dashboard')->hotkey('alt+1');
    $palette->addAction('users')->title('Manage users')->mdIcon('group')->route('users.index')->can('manage-users');
});

// In your layout, before </body>: <x-ninja-keys />

Setup

The service provider and the NinjaKeys facade are auto-discovered. The JavaScript bundle is published to public/vendor/ninja-keys on composer update through Laravel's laravel-assets step. If your app does not run that step, publish it once. Until it is published, the palette loads ninja-keys from jsDelivr.

  1. 1 composer require amjadiqbal/laravel-ninja-keys
  2. 2 php artisan vendor:publish --tag=ninja-keys-assets

Render the palette

Put the component once in your main layout, before </body>. Any element with data-ninja-keys-open opens the palette on click, optionally straight into a submenu.

<x-ninja-keys />

{{-- or as a directive --}}
@ninjaKeys

<button data-ninja-keys-open>Search <kbd>⌘K</kbd></button>
<button data-ninja-keys-open="theme">Change theme…</button>

Register actions

Register actions in a controller for page-specific actions, or with define() in a service provider for the global ones. has(), forget() and clear() let a page remove or replace global actions.

NinjaKeys::addAction('new-invoice')
    ->title('New invoice')
    ->section('Billing')
    ->mdIcon('receipt_long')
    ->keywords('bill charge create')
    ->hotkey('alt+n')
    ->route('invoices.create');

Only show what the user may do

Hidden actions and their sub-actions are removed on the server; they never appear in the page source.

Gate::define('manage-users', fn (User $user) => $user->is_admin);

NinjaKeys::addAction('users')->title('Users')->can('manage-users')->children(['invite']);
NinjaKeys::addAction('invite')->title('Invite a user')->parent('users')->route('users.invite');

Configuration

Publish the config with php artisan vendor:publish --tag=ninja-keys-config.

  • script: local (the bundled file), cdn (jsDelivr) or null (you import ninja-keys with Vite).
  • theme: auto, light or dark.
  • openHotkey: cmd+k,ctrl+k by default.
  • livewire_navigate: make href() and route() actions use Livewire.navigate().
  • onOpen, onChange, onSelected: names of global functions to call.
  • Both scripts carry the CSP nonce when you use Vite::useCspNonce().

Hire me

Need this built into your app, or something custom?

I wrote Laravel Ninja Keys. I can wire it into your Laravel app, extend it for your case, or build the feature around it. Tell me what you need and you'll get a plan and a quote, free.

Support

Found a bug or want a feature? Open an issue on GitHub. For help adding it to your own project, use the quote form above.

Need something built?

Free quote in 60 seconds