Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Spotlight Laravel Package

livewire-ui/spotlight

Livewire UI Spotlight adds a keyboard-driven command palette to Laravel Livewire apps. Let users quickly search, navigate, and run custom actions from a polished Spotlight-style modal with flexible configuration.

View on GitHub
Deep Wiki
Context7

Getting Started

What it is: wire-elements/spotlight (v2.0.4) is a Livewire/Alpine command palette for Laravel — the Cmd+K / Ctrl+K search overlay that fuzzy-matches user-defined "commands" and executes the matching closure server-side.

Minimal setup:

composer require wire-elements/spotlight

Add the component once to your main layout, before </body>:

<livewire:spotlight />

Publish the config (and views, if you plan to restyle the overlay):

php artisan vendor:publish --tag=spotlight-config
php artisan vendor:publish --tag=spotlight-views

Where to look first: the package's config/spotlight.php for the search/limit behaviour, and the README's Spotlight::register() signature — everything else in the package is plumbing around that one call.

First use case: register two or three commands in a dedicated provider and use that as your template. Put it in app/Providers/SpotlightServiceProvider.php and register it in bootstrap/providers.php:

use LivewireUI\Spotlight\Spotlight;

public function boot(): void
{
    Spotlight::register('viewInvoices', 'View Invoices', function () {
        return redirect()->route('invoices.index');
    })->description('Browse all your invoices')
      ->synonyms(['billing', 'payments']);
}

Hit Cmd+K and you should see it immediately. If nothing appears, the component is missing from the layout or the provider isn't booting — that covers ~90% of first-run issues.

Version note: 2.0.4 is a Laravel 13.x compatibility bump on top of 2.0.3. There are no API changes, deprecations, or breaking changes in this release — it's a maintenance signal (the package tracks current Laravel/Livewire) rather than a feature release. Pin ^2.0 and upgrade freely.

Implementation Patterns

Group commands by domain, not by file size. One provider per feature area (InvoiceSpotlightProvider, AdminSpotlightProvider) registered conditionally keeps boot cost low and makes it obvious where to add the next command.

Wrap registration in a shared helper for consistency. Descriptions, synonyms and icons are easy to forget; a thin wrapper enforces your conventions:

Spotlight::register('viewInvoices', 'View Invoices', fn () => redirect()->route('invoices.index'))
    ->description('Browse all your invoices')
    ->synonyms(['billing', 'payments'])
    ->icon('heroicon-o-document-text');

Use sub-commands for "navigate then act" flows. Registering a parent and hanging related actions off it keeps the result list shallow — a single "Invoices" entry that expands into View, Create, Export reads much better than three flat top-level entries:

Spotlight::register('invoices', 'Invoices', fn () => redirect()->route('invoices.index'))
    ->subCommands([
        // SpotlightCommand::make('invoices.create', 'Create Invoice', fn () => redirect()->route('invoices.create')),
    ]);

Express ordering with ->dependencies([...]) when a command only makes sense after another one has been surfaced; it's the built-in mechanism for contextual relevance instead of hand-rolling filters.

Return actions, don't perform them at registration time. The closure runs only on selection. Anything with a side effect — redirects, dispatch(), toasts, DB writes — belongs inside the closure:

Spotlight::register('flushCache', 'Flush Cache', function () {
    Cache::flush();
    $this->dispatch('notify', message: 'Cache flushed');
    return redirect()->back();
});

Pair it with wire-elements/modal. Spotlight is best as a launcher: the search overlay selects an entry, the closure dispatches an event your modal listens for, and the actual form lives in a Livewire component. This keeps the palette stateless and testable.

Drive it from the keyboard and from the UI. Beyond the built-in Cmd+K binding, expose it on a dashboard search input or a nav button so mouse users discover it — otherwise nobody finds the feature. Use the package's documented toggle event rather than manipulating the DOM directly, so it survives Livewire navigation.

Test by calling the registry, not the UI. Assert Spotlight::search('inv') returns your command's ID. That gives you fast coverage of registration, synonyms, and search behaviour without booting a browser.

Gotchas and Tips

Authorize inside every closure. Registration is not a gate. Any authenticated user who can reach the Livewire component can execute a registered command by ID, so a viewInvoices registered unguarded is reachable by a user with no invoice permissions. Treat the closure as a controller action:

Spotlight::register('viewInvoices', 'View Invoices', function () {
    abort_unless(Gate::allows('viewAny', Invoice::class), 403);
    return redirect()->route('invoices.index');
});

For anything privileged, prefer not registering the command at all for users who can't use it (check in the provider) — cleaner UI and one less attack surface.

Never touch the database at boot. Registration closures are cheap, but a query run while building the command list (e.g. listing all users as commands) executes on every request in that provider. Instead, register one command that opens a searchable Livewire component, and query inside it.

Keep command IDs stable and namespaced. IDs are the contract between the front end and the server-side registry. Renaming 'viewInvoices' silently breaks bookmarks and any tests that reference it; namespacing ('invoices.view') makes later renames obvious in review.

Tailwind needs the vendor path. If the overlay renders unstyled, your tailwind.config.js content globs are missing the package views:

content: [
    './resources/**/*.blade.php',
    './vendor/wire-elements/spotlight/resources/**/*.blade.php',
],

This is the single most common "it's installed but looks broken" report. Publish the views if you need to restyle beyond the default.

Closures and Livewire serialization. Avoid capturing request-scoped objects ($request, Eloquent models, closures over $this) in a command closure — on a subsequent Livewire round-trip the context may differ or be unserializable. Capture scalars or resolve from the container inside the closure.

wire:navigate and SPA behaviour. Mounting the component in a persistent layout generally survives navigation, but keyboard listeners can double-bind if the layout is re-rendered. If Cmd+K fires twice (two overlays, or an action executing twice), the component is mounted twice — check for it in both app.blade.php and a nested layout.

Keyboard conflicts. Cmd+K is claimed by browser search in some contexts and by editors like TinyMCE inside your app. If you have an in-page editor, scope the shortcut or provide an alternative binding via the published config; don't fight the browser for Cmd/Ctrl+K in a textarea.

Search is server-side fuzzy matching, not a full search engine. It matches on command name, description and synonyms. If results feel weak, the fix is almost always better ->synonyms([...]) (users type "bill", not "invoice"), not a deeper backend integration.

Multi-tenancy. Commands registered from a shared provider see the current tenant automatically, but if you cache anything (command lists, search results), key the cache by tenant — otherwise tenant B sees tenant A's command names.

Extension points worth knowing: published config (search behaviour, limits), published Blade views (markup/markup classes), and the registry itself — Spotlight::search() is public, so a custom Livewire search input can reuse the exact same command resolution as the palette.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource